@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.
- package/.swcrc +11 -0
- package/bin/build-browser +40 -0
- package/bin/copy-migrations +9 -0
- package/db.sqlite +0 -0
- package/default-db.sqlite +0 -0
- package/migrations/.force-copy-windows +0 -0
- package/migrations/1548957970627_remove-db-version.sql +5 -0
- package/migrations/1550601598648_payees.sql +23 -0
- package/migrations/1555786194328_remove_category_group_unique.sql +25 -0
- package/migrations/1561751833510_indexes.sql +7 -0
- package/migrations/1567699552727_budget.sql +38 -0
- package/migrations/1582384163573_cleared.sql +6 -0
- package/migrations/1597756566448_rules.sql +10 -0
- package/migrations/1608652596043_parent_field.sql +13 -0
- package/migrations/1608652596044_trans_views.sql +56 -0
- package/migrations/1612625548236_optimize.sql +7 -0
- package/migrations/1614782639336_trans_views2.sql +33 -0
- package/migrations/1615745967948_meta.sql +10 -0
- package/migrations/1616167010796_accounts_order.sql +5 -0
- package/migrations/1618975177358_schedules.sql +28 -0
- package/migrations/1632571489012_remove_cache.js +136 -0
- package/migrations/1679728867040_rules_conditions.sql +5 -0
- package/migrations/1681115033845_add_schedule_name.sql +5 -0
- package/migrations/1682974838138_remove_payee_rules.sql +5 -0
- package/migrations/1685007876842_add_category_hidden.sql +6 -0
- package/migrations/1686139660866_remove_account_type.sql +5 -0
- package/migrations/1688749527273_transaction_filters.sql +10 -0
- package/migrations/1688841238000_add_account_type.sql +5 -0
- package/migrations/1691233396000_add_schedule_next_date_tombstone.sql +5 -0
- package/migrations/1694438752000_add_goal_targets.sql +7 -0
- package/migrations/1697046240000_add_reconciled.sql +5 -0
- package/migrations/1704572023730_add_account_sync_source.sql +5 -0
- package/migrations/1704572023731_add_missing_goCardless_sync_source.sql +9 -0
- package/migrations/1707267033000_reports.sql +28 -0
- package/migrations/1712784523000_unhide_input_group.sql +8 -0
- package/migrations/1716359441000_include_current.sql +5 -0
- package/migrations/1720310586000_link_transfer_schedules.sql +19 -0
- package/migrations/1720664867241_add_payee_favorite.sql +5 -0
- package/migrations/1720665000000_goal_context.sql +6 -0
- package/migrations/1722717601000_reports_move_selected_categories.js +55 -0
- package/migrations/1722804019000_create_dashboard_table.js +69 -0
- package/migrations/1723665565000_prefs.js +59 -0
- package/migrations/1730744182000_fix_dashboard_table.sql +7 -0
- package/migrations/1736640000000_custom_report_sorting.sql +7 -0
- package/migrations/1737158400000_add_learn_categories_to_payees.sql +5 -0
- package/migrations/1738491452000_sorting_rename.sql +13 -0
- package/migrations/1739139550000_bank_sync_page.sql +7 -0
- package/migrations/1740506588539_add_last_reconciled_at.sql +5 -0
- package/migrations/1745425408000_update_budgetType_pref.sql +7 -0
- package/migrations/1749799110000_add_tags.sql +10 -0
- package/migrations/1749799110001_tags_tombstone.sql +5 -0
- package/migrations/1754611200000_add_category_template_settings.sql +5 -0
- package/migrations/1759260219000_add_trim_interval_report_setting.sql +6 -0
- package/migrations/1759842823172_add_isGlobal_to_preferences.sql +1 -0
- package/migrations/1762178745667_rename_csv_skip_lines_pref.sql +8 -0
- package/migrations/1765518577215_multiple_dashboards.js +30 -0
- package/migrations/1768872504000_add_payee_locations.sql +21 -0
- package/package.json +128 -0
- package/src/mocks/arbitrary-schema.ts +162 -0
- package/src/mocks/budget.ts +901 -0
- package/src/mocks/files/8859-1.qfx +63 -0
- package/src/mocks/files/best.data-ever$.QFX +124 -0
- package/src/mocks/files/big.data.QiF +91 -0
- package/src/mocks/files/budgets/.commit-to-git +0 -0
- package/src/mocks/files/camt/camt.053.payee-memo.xml +127 -0
- package/src/mocks/files/camt/camt.053.xml +463 -0
- package/src/mocks/files/credit-card.ofx +11 -0
- package/src/mocks/files/data-multi-decimal.ofx +64 -0
- package/src/mocks/files/data-payee-memo.ofx +75 -0
- package/src/mocks/files/data-payee-memo.qif +17 -0
- package/src/mocks/files/data.ofx +124 -0
- package/src/mocks/files/data.qfx +124 -0
- package/src/mocks/files/data.qif +91 -0
- package/src/mocks/files/default-budget-template/db.sqlite +0 -0
- package/src/mocks/files/default-budget-template/metadata.json +6 -0
- package/src/mocks/files/html-vals.qfx +17 -0
- package/src/mocks/index.ts +221 -0
- package/src/mocks/migrations/1508717984291_up_add-poop.sql +13 -0
- package/src/mocks/migrations/1508718036311_up_modify-poop.sql +2 -0
- package/src/mocks/migrations/1508727787513_remove-is_income.sql +15 -0
- package/src/mocks/random.ts +16 -0
- package/src/mocks/setup.ts +180 -0
- package/src/mocks/spreadsheet.ts +101 -0
- package/src/mocks/util.ts +82 -0
- package/src/platform/client/connection/README.md +3 -0
- package/src/platform/client/connection/__mocks__/index.ts +67 -0
- package/src/platform/client/connection/index-types.ts +95 -0
- package/src/platform/client/connection/index.browser.ts +213 -0
- package/src/platform/client/connection/index.ts +155 -0
- package/src/platform/client/undo/index.ts +59 -0
- package/src/platform/exceptions/__mocks__/index.ts +7 -0
- package/src/platform/exceptions/index.ts +9 -0
- package/src/platform/server/asyncStorage/__mocks__/index.ts +50 -0
- package/src/platform/server/asyncStorage/index-types.ts +35 -0
- package/src/platform/server/asyncStorage/index.api.ts +2 -0
- package/src/platform/server/asyncStorage/index.electron.ts +88 -0
- package/src/platform/server/asyncStorage/index.ts +126 -0
- package/src/platform/server/connection/README.md +3 -0
- package/src/platform/server/connection/__mocks__/index.ts +15 -0
- package/src/platform/server/connection/index-types.ts +20 -0
- package/src/platform/server/connection/index.api.ts +13 -0
- package/src/platform/server/connection/index.electron.ts +102 -0
- package/src/platform/server/connection/index.ts +154 -0
- package/src/platform/server/fetch/__mocks__/index.ts +3 -0
- package/src/platform/server/fetch/index.api.ts +1 -0
- package/src/platform/server/fetch/index.electron.ts +18 -0
- package/src/platform/server/fetch/index.ts +20 -0
- package/src/platform/server/fs/index.api.ts +198 -0
- package/src/platform/server/fs/index.electron.ts +208 -0
- package/src/platform/server/fs/index.test.ts +117 -0
- package/src/platform/server/fs/index.ts +416 -0
- package/src/platform/server/fs/path-join.api.ts +1 -0
- package/src/platform/server/fs/path-join.electron.ts +1 -0
- package/src/platform/server/fs/path-join.ts +97 -0
- package/src/platform/server/fs/shared.ts +33 -0
- package/src/platform/server/indexeddb/index.ts +115 -0
- package/src/platform/server/log/index.ts +43 -0
- package/src/platform/server/sqlite/index.api.ts +2 -0
- package/src/platform/server/sqlite/index.electron.ts +134 -0
- package/src/platform/server/sqlite/index.test.ts +108 -0
- package/src/platform/server/sqlite/index.ts +241 -0
- package/src/platform/server/sqlite/normalise.ts +9 -0
- package/src/platform/server/sqlite/unicodeLike.test.ts +58 -0
- package/src/platform/server/sqlite/unicodeLike.ts +31 -0
- package/src/server/__mocks__/post.ts +9 -0
- package/src/server/__snapshots__/main.test.ts.snap +199 -0
- package/src/server/__snapshots__/sheet.test.ts.snap +9 -0
- package/src/server/accounts/__snapshots__/sync.test.ts.snap +136 -0
- package/src/server/accounts/app-bank-sync.test.ts +136 -0
- package/src/server/accounts/app.ts +1294 -0
- package/src/server/accounts/link.ts +25 -0
- package/src/server/accounts/payees.ts +36 -0
- package/src/server/accounts/sync.test.ts +679 -0
- package/src/server/accounts/sync.ts +1168 -0
- package/src/server/accounts/title/index.ts +60 -0
- package/src/server/accounts/title/lower-case.ts +93 -0
- package/src/server/accounts/title/specials.ts +21 -0
- package/src/server/admin/app.ts +241 -0
- package/src/server/api-models.ts +244 -0
- package/src/server/api.test.ts +36 -0
- package/src/server/api.ts +1030 -0
- package/src/server/app.ts +91 -0
- package/src/server/aql/compiler.test.ts +966 -0
- package/src/server/aql/compiler.ts +1222 -0
- package/src/server/aql/exec.test.ts +289 -0
- package/src/server/aql/exec.ts +128 -0
- package/src/server/aql/index.ts +41 -0
- package/src/server/aql/schema/executors.test.ts +420 -0
- package/src/server/aql/schema/executors.ts +345 -0
- package/src/server/aql/schema/index.test.ts +67 -0
- package/src/server/aql/schema/index.ts +409 -0
- package/src/server/aql/schema-helpers.test.ts +242 -0
- package/src/server/aql/schema-helpers.ts +208 -0
- package/src/server/aql/views.test.ts +62 -0
- package/src/server/aql/views.ts +57 -0
- package/src/server/auth/app.ts +387 -0
- package/src/server/bench.ts +29 -0
- package/src/server/budget/actions.ts +686 -0
- package/src/server/budget/app.ts +469 -0
- package/src/server/budget/base.test.ts +340 -0
- package/src/server/budget/base.ts +339 -0
- package/src/server/budget/category-template-context.test.ts +1658 -0
- package/src/server/budget/category-template-context.ts +862 -0
- package/src/server/budget/cleanup-template.pegjs +27 -0
- package/src/server/budget/cleanup-template.ts +408 -0
- package/src/server/budget/envelope.ts +403 -0
- package/src/server/budget/goal-template.pegjs +110 -0
- package/src/server/budget/goal-template.ts +309 -0
- package/src/server/budget/report.ts +308 -0
- package/src/server/budget/schedule-template.test.ts +184 -0
- package/src/server/budget/schedule-template.ts +351 -0
- package/src/server/budget/statements.ts +60 -0
- package/src/server/budget/template-notes.test.ts +393 -0
- package/src/server/budget/template-notes.ts +323 -0
- package/src/server/budget/util.ts +25 -0
- package/src/server/budgetfiles/__snapshots__/backups.test.ts.snap +101 -0
- package/src/server/budgetfiles/app.ts +672 -0
- package/src/server/budgetfiles/backups.test.ts +79 -0
- package/src/server/budgetfiles/backups.ts +251 -0
- package/src/server/cloud-storage.ts +467 -0
- package/src/server/dashboard/app.ts +373 -0
- package/src/server/db/__snapshots__/index.test.ts.snap +271 -0
- package/src/server/db/index.test.ts +300 -0
- package/src/server/db/index.ts +855 -0
- package/src/server/db/mappings.ts +59 -0
- package/src/server/db/sort.ts +58 -0
- package/src/server/db/types/index.ts +342 -0
- package/src/server/db/util.ts +36 -0
- package/src/server/encryption/app.ts +133 -0
- package/src/server/encryption/encryption-internals.api.ts +2 -0
- package/src/server/encryption/encryption-internals.electron.ts +89 -0
- package/src/server/encryption/encryption-internals.ts +109 -0
- package/src/server/encryption/encryption.test.ts +19 -0
- package/src/server/encryption/index.test.ts +19 -0
- package/src/server/encryption/index.ts +89 -0
- package/src/server/errors.ts +110 -0
- package/src/server/filters/app.ts +191 -0
- package/src/server/importers/actual.ts +49 -0
- package/src/server/importers/index.ts +58 -0
- package/src/server/importers/ynab4-types.ts +163 -0
- package/src/server/importers/ynab4.ts +470 -0
- package/src/server/importers/ynab5-types.ts +290 -0
- package/src/server/importers/ynab5.ts +1193 -0
- package/src/server/main-app.ts +25 -0
- package/src/server/main.test.ts +392 -0
- package/src/server/main.ts +336 -0
- package/src/server/migrate/__snapshots__/migrations.test.ts.snap +17 -0
- package/src/server/migrate/cli.ts +100 -0
- package/src/server/migrate/migrations.test.ts +81 -0
- package/src/server/migrate/migrations.ts +192 -0
- package/src/server/models.ts +184 -0
- package/src/server/mutators.ts +139 -0
- package/src/server/notes/app.ts +18 -0
- package/src/server/payees/app.ts +351 -0
- package/src/server/polyfills.ts +26 -0
- package/src/server/post.ts +219 -0
- package/src/server/preferences/app.ts +249 -0
- package/src/server/prefs.ts +91 -0
- package/src/server/reports/app.ts +187 -0
- package/src/server/rules/action.ts +344 -0
- package/src/server/rules/app.ts +193 -0
- package/src/server/rules/condition.ts +436 -0
- package/src/server/rules/customFunctions.ts +61 -0
- package/src/server/rules/formula-action.test.ts +175 -0
- package/src/server/rules/handlebars-helpers.ts +131 -0
- package/src/server/rules/index.test.ts +1095 -0
- package/src/server/rules/index.ts +22 -0
- package/src/server/rules/rule-indexer.ts +89 -0
- package/src/server/rules/rule-utils.ts +274 -0
- package/src/server/rules/rule.ts +193 -0
- package/src/server/schedules/app.test.ts +502 -0
- package/src/server/schedules/app.ts +644 -0
- package/src/server/schedules/find-schedules.ts +391 -0
- package/src/server/server-config.ts +59 -0
- package/src/server/sheet.test.ts +101 -0
- package/src/server/sheet.ts +280 -0
- package/src/server/spreadsheet/__snapshots__/spreadsheet.test.ts.snap +5 -0
- package/src/server/spreadsheet/app.ts +54 -0
- package/src/server/spreadsheet/globals.ts +13 -0
- package/src/server/spreadsheet/graph-data-structure.ts +165 -0
- package/src/server/spreadsheet/scratch +60 -0
- package/src/server/spreadsheet/spreadsheet.test.ts +191 -0
- package/src/server/spreadsheet/spreadsheet.ts +523 -0
- package/src/server/spreadsheet/util.ts +15 -0
- package/src/server/sql/init.sql +88 -0
- package/src/server/sync/__snapshots__/sync.test.ts.snap +31 -0
- package/src/server/sync/app.ts +29 -0
- package/src/server/sync/encoder.ts +129 -0
- package/src/server/sync/index.ts +820 -0
- package/src/server/sync/make-test-message.ts +19 -0
- package/src/server/sync/migrate.test.ts +169 -0
- package/src/server/sync/migrate.ts +48 -0
- package/src/server/sync/repair.ts +39 -0
- package/src/server/sync/reset.ts +91 -0
- package/src/server/sync/sync.property.test.ts +385 -0
- package/src/server/sync/sync.test.ts +349 -0
- package/src/server/sync/utils.ts +3 -0
- package/src/server/tags/app.ts +101 -0
- package/src/server/tests/mockData.json +9352 -0
- package/src/server/tests/mockSyncServer.ts +119 -0
- package/src/server/tools/app.ts +152 -0
- package/src/server/transactions/__snapshots__/transaction-rules.test.ts.snap +173 -0
- package/src/server/transactions/__snapshots__/transfer.test.ts.snap +655 -0
- package/src/server/transactions/app.ts +136 -0
- package/src/server/transactions/export/export-to-csv.ts +132 -0
- package/src/server/transactions/import/__snapshots__/parse-file.test.ts.snap +1582 -0
- package/src/server/transactions/import/ofx2json.test.ts +33 -0
- package/src/server/transactions/import/ofx2json.ts +157 -0
- package/src/server/transactions/import/parse-file.test.ts +224 -0
- package/src/server/transactions/import/parse-file.ts +286 -0
- package/src/server/transactions/import/qif2json.ts +110 -0
- package/src/server/transactions/import/xmlcamt2json.ts +168 -0
- package/src/server/transactions/index.ts +196 -0
- package/src/server/transactions/merge.test.ts +370 -0
- package/src/server/transactions/merge.ts +139 -0
- package/src/server/transactions/transaction-rules.test.ts +994 -0
- package/src/server/transactions/transaction-rules.ts +1038 -0
- package/src/server/transactions/transfer.test.ts +221 -0
- package/src/server/transactions/transfer.ts +173 -0
- package/src/server/undo.ts +271 -0
- package/src/server/update.ts +37 -0
- package/src/server/util/budget-name.ts +61 -0
- package/src/server/util/custom-sync-mapping.ts +48 -0
- package/src/server/util/rschedule.ts +9 -0
- package/src/shared/__mocks__/platform.ts +7 -0
- package/src/shared/__snapshots__/months.test.ts.snap +21 -0
- package/src/shared/arithmetic.test.ts +112 -0
- package/src/shared/arithmetic.ts +170 -0
- package/src/shared/async.test.ts +135 -0
- package/src/shared/async.ts +76 -0
- package/src/shared/constants.ts +5 -0
- package/src/shared/currencies.ts +70 -0
- package/src/shared/dashboard.ts +260 -0
- package/src/shared/environment.ts +18 -0
- package/src/shared/errors.ts +195 -0
- package/src/shared/locale.ts +27 -0
- package/src/shared/location-utils.test.ts +69 -0
- package/src/shared/location-utils.ts +49 -0
- package/src/shared/months.test.ts +5 -0
- package/src/shared/months.ts +485 -0
- package/src/shared/normalisation.ts +6 -0
- package/src/shared/platform.electron.ts +21 -0
- package/src/shared/platform.ts +20 -0
- package/src/shared/query.ts +176 -0
- package/src/shared/rules.test.ts +56 -0
- package/src/shared/rules.ts +371 -0
- package/src/shared/schedules.test.ts +570 -0
- package/src/shared/schedules.ts +560 -0
- package/src/shared/test-helpers.ts +156 -0
- package/src/shared/transactions.test.ts +275 -0
- package/src/shared/transactions.ts +433 -0
- package/src/shared/transfer.test.ts +75 -0
- package/src/shared/transfer.ts +16 -0
- package/src/shared/user.ts +4 -0
- package/src/shared/util.test.ts +240 -0
- package/src/shared/util.ts +633 -0
- package/src/types/api-handlers.ts +287 -0
- package/src/types/budget.ts +8 -0
- package/src/types/file.ts +47 -0
- package/src/types/handlers.ts +46 -0
- package/src/types/models/account.ts +24 -0
- package/src/types/models/bank-sync.ts +23 -0
- package/src/types/models/bank.ts +6 -0
- package/src/types/models/category-group.ts +11 -0
- package/src/types/models/category.ts +13 -0
- package/src/types/models/dashboard.ts +199 -0
- package/src/types/models/gocardless.ts +84 -0
- package/src/types/models/import-transaction.ts +56 -0
- package/src/types/models/index.ts +23 -0
- package/src/types/models/nearby-payee.ts +7 -0
- package/src/types/models/note.ts +4 -0
- package/src/types/models/openid.ts +8 -0
- package/src/types/models/payee-location.ts +8 -0
- package/src/types/models/payee.ts +10 -0
- package/src/types/models/pluggyai.ts +19 -0
- package/src/types/models/reports.ts +144 -0
- package/src/types/models/rule.ts +174 -0
- package/src/types/models/schedule.ts +49 -0
- package/src/types/models/simplefin.ts +28 -0
- package/src/types/models/tags.ts +6 -0
- package/src/types/models/templates.ts +135 -0
- package/src/types/models/transaction-filter.ts +9 -0
- package/src/types/models/transaction.ts +39 -0
- package/src/types/models/user-access.ts +10 -0
- package/src/types/models/user.ts +25 -0
- package/src/types/prefs.ts +167 -0
- package/src/types/server-events.ts +86 -0
- package/src/types/server-handlers.ts +27 -0
- package/src/types/util.ts +26 -0
- package/tsconfig.json +34 -0
- package/typings/pegjs.ts +1 -0
- package/typings/process-worker.ts +12 -0
- package/typings/vite-plugin-peggy-loader.ts +1 -0
- package/typings/window.ts +62 -0
- package/vite.config.ts +109 -0
- package/vite.desktop.config.ts +59 -0
- package/vitest.config.ts +43 -0
- package/vitest.web.config.ts +38 -0
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
// @ts-strict-ignore
|
|
2
|
+
import type { GlobalPrefsJson } from '../../../types/prefs';
|
|
3
|
+
import { getDatabase } from '../indexeddb';
|
|
4
|
+
|
|
5
|
+
import type * as T from './index-types';
|
|
6
|
+
|
|
7
|
+
export const init: T.Init = function () {
|
|
8
|
+
// No need to initialise in the browser
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
export const getItem: T.GetItem = async function (key) {
|
|
12
|
+
const db = await getDatabase();
|
|
13
|
+
|
|
14
|
+
const transaction = db.transaction(['asyncStorage'], 'readonly');
|
|
15
|
+
const objectStore = transaction.objectStore('asyncStorage');
|
|
16
|
+
|
|
17
|
+
return new Promise((resolve, reject) => {
|
|
18
|
+
const req = objectStore.get(key);
|
|
19
|
+
req.onerror = e => reject(e);
|
|
20
|
+
// @ts-expect-error fix me
|
|
21
|
+
req.onsuccess = e => resolve(e.target.result);
|
|
22
|
+
});
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
export const setItem: T.SetItem = async function (key, value) {
|
|
26
|
+
const db = await getDatabase();
|
|
27
|
+
|
|
28
|
+
const transaction = db.transaction(['asyncStorage'], 'readwrite');
|
|
29
|
+
const objectStore = transaction.objectStore('asyncStorage');
|
|
30
|
+
|
|
31
|
+
void new Promise((resolve, reject) => {
|
|
32
|
+
const req = objectStore.put(value, key);
|
|
33
|
+
req.onerror = e => reject(e);
|
|
34
|
+
req.onsuccess = () => resolve(undefined);
|
|
35
|
+
transaction.commit();
|
|
36
|
+
});
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
export const removeItem: T.RemoveItem = async function (key) {
|
|
40
|
+
const db = await getDatabase();
|
|
41
|
+
|
|
42
|
+
const transaction = db.transaction(['asyncStorage'], 'readwrite');
|
|
43
|
+
const objectStore = transaction.objectStore('asyncStorage');
|
|
44
|
+
|
|
45
|
+
return new Promise((resolve, reject) => {
|
|
46
|
+
const req = objectStore.delete(key);
|
|
47
|
+
req.onerror = e => reject(e);
|
|
48
|
+
req.onsuccess = () => resolve(undefined);
|
|
49
|
+
transaction.commit();
|
|
50
|
+
});
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
export async function multiGet<K extends readonly (keyof GlobalPrefsJson)[]>(
|
|
54
|
+
keys: K,
|
|
55
|
+
): Promise<{ [P in K[number]]: GlobalPrefsJson[P] }> {
|
|
56
|
+
const db = await getDatabase();
|
|
57
|
+
|
|
58
|
+
const transaction = db.transaction(['asyncStorage'], 'readonly');
|
|
59
|
+
const objectStore = transaction.objectStore('asyncStorage');
|
|
60
|
+
|
|
61
|
+
const results = await Promise.all(
|
|
62
|
+
keys.map(key => {
|
|
63
|
+
return new Promise<[K[number], GlobalPrefsJson[K[number]]]>(
|
|
64
|
+
(resolve, reject) => {
|
|
65
|
+
const req = objectStore.get(key);
|
|
66
|
+
req.onerror = e => reject(e);
|
|
67
|
+
req.onsuccess = e => {
|
|
68
|
+
const target = e.target as IDBRequest<GlobalPrefsJson[K[number]]>;
|
|
69
|
+
resolve([key, target.result]);
|
|
70
|
+
};
|
|
71
|
+
},
|
|
72
|
+
);
|
|
73
|
+
}),
|
|
74
|
+
);
|
|
75
|
+
|
|
76
|
+
transaction.commit();
|
|
77
|
+
|
|
78
|
+
// Convert the array of tuples to an object with properly typed properties
|
|
79
|
+
return results.reduce(
|
|
80
|
+
(acc, [key, value]) => {
|
|
81
|
+
acc[key] = value;
|
|
82
|
+
return acc;
|
|
83
|
+
},
|
|
84
|
+
{} as { [P in K[number]]: GlobalPrefsJson[P] },
|
|
85
|
+
);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export const multiSet: T.MultiSet = async function (keyValues) {
|
|
89
|
+
const db = await getDatabase();
|
|
90
|
+
|
|
91
|
+
const transaction = db.transaction(['asyncStorage'], 'readwrite');
|
|
92
|
+
const objectStore = transaction.objectStore('asyncStorage');
|
|
93
|
+
|
|
94
|
+
const promise = Promise.all(
|
|
95
|
+
keyValues.map(([key, value]) => {
|
|
96
|
+
return new Promise((resolve, reject) => {
|
|
97
|
+
const req = objectStore.put(value, key);
|
|
98
|
+
req.onerror = e => reject(e);
|
|
99
|
+
req.onsuccess = () => resolve(undefined);
|
|
100
|
+
});
|
|
101
|
+
}),
|
|
102
|
+
);
|
|
103
|
+
|
|
104
|
+
transaction.commit();
|
|
105
|
+
await promise;
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
export const multiRemove: T.MultiRemove = async function (keys) {
|
|
109
|
+
const db = await getDatabase();
|
|
110
|
+
|
|
111
|
+
const transaction = db.transaction(['asyncStorage'], 'readwrite');
|
|
112
|
+
const objectStore = transaction.objectStore('asyncStorage');
|
|
113
|
+
|
|
114
|
+
const promise = Promise.all(
|
|
115
|
+
keys.map(key => {
|
|
116
|
+
return new Promise((resolve, reject) => {
|
|
117
|
+
const req = objectStore.delete(key);
|
|
118
|
+
req.onerror = e => reject(e);
|
|
119
|
+
req.onsuccess = () => resolve(undefined);
|
|
120
|
+
});
|
|
121
|
+
}),
|
|
122
|
+
);
|
|
123
|
+
|
|
124
|
+
transaction.commit();
|
|
125
|
+
await promise;
|
|
126
|
+
};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type * as T from '../index-types';
|
|
2
|
+
|
|
3
|
+
let events = [];
|
|
4
|
+
|
|
5
|
+
export const init: T.Init = function () {
|
|
6
|
+
// No need to initialise in tests
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
export const send: T.Send = function (type, args) {
|
|
10
|
+
events.push([type, args]);
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
export const resetEvents: T.ResetEvents = function () {
|
|
14
|
+
events = [];
|
|
15
|
+
};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { Handlers } from '../../../types/handlers';
|
|
2
|
+
import type { ServerEvents } from '../../../types/server-events';
|
|
3
|
+
|
|
4
|
+
export declare function init(
|
|
5
|
+
channel: Window | number, // in electron the port number, in web the worker
|
|
6
|
+
handlers: Handlers,
|
|
7
|
+
): void;
|
|
8
|
+
export type Init = typeof init;
|
|
9
|
+
|
|
10
|
+
export declare function send<K extends keyof ServerEvents>(
|
|
11
|
+
type: K,
|
|
12
|
+
args?: ServerEvents[K],
|
|
13
|
+
): void;
|
|
14
|
+
export type Send = typeof send;
|
|
15
|
+
|
|
16
|
+
export declare function getNumClients(): number;
|
|
17
|
+
export type GetNumClients = typeof getNumClients;
|
|
18
|
+
|
|
19
|
+
export declare function resetEvents(): void;
|
|
20
|
+
export type ResetEvents = typeof resetEvents;
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
// @ts-strict-ignore
|
|
2
|
+
import { APIError } from '../../../server/errors';
|
|
3
|
+
import { isMutating, runHandler } from '../../../server/mutators';
|
|
4
|
+
import { captureException } from '../../exceptions';
|
|
5
|
+
import { logger } from '../log';
|
|
6
|
+
|
|
7
|
+
import type * as T from './index-types';
|
|
8
|
+
|
|
9
|
+
function coerceError(error) {
|
|
10
|
+
if (error.type && error.type === 'APIError') {
|
|
11
|
+
return error;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
return { type: 'ServerError', message: error.message, cause: error };
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export const init: T.Init = function (_socketName, handlers) {
|
|
18
|
+
process.parentPort.on('message', ({ data }) => {
|
|
19
|
+
const { id, name, args, undoTag, catchErrors } = data;
|
|
20
|
+
|
|
21
|
+
if (handlers[name]) {
|
|
22
|
+
runHandler(handlers[name], args, { undoTag, name }).then(
|
|
23
|
+
result => {
|
|
24
|
+
if (catchErrors) {
|
|
25
|
+
result = { data: result, error: null };
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
process.parentPort.postMessage({
|
|
29
|
+
type: 'reply',
|
|
30
|
+
id,
|
|
31
|
+
result,
|
|
32
|
+
mutated:
|
|
33
|
+
isMutating(handlers[name]) && name !== 'undo' && name !== 'redo',
|
|
34
|
+
undoTag,
|
|
35
|
+
});
|
|
36
|
+
},
|
|
37
|
+
nativeError => {
|
|
38
|
+
const error = coerceError(nativeError);
|
|
39
|
+
|
|
40
|
+
if (name.startsWith('api/')) {
|
|
41
|
+
// The API is newer and does automatically forward
|
|
42
|
+
// errors
|
|
43
|
+
process.parentPort.postMessage({
|
|
44
|
+
type: 'reply',
|
|
45
|
+
id,
|
|
46
|
+
error,
|
|
47
|
+
});
|
|
48
|
+
} else if (catchErrors) {
|
|
49
|
+
process.parentPort.postMessage({
|
|
50
|
+
type: 'reply',
|
|
51
|
+
id,
|
|
52
|
+
result: { error, data: null },
|
|
53
|
+
});
|
|
54
|
+
} else {
|
|
55
|
+
process.parentPort.postMessage({ type: 'error', id, error });
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
if (error.type === 'ServerError' && name !== 'api/load-budget') {
|
|
59
|
+
captureException(nativeError);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
if (!catchErrors) {
|
|
63
|
+
// Notify the frontend that something bad happend
|
|
64
|
+
send('server-error');
|
|
65
|
+
}
|
|
66
|
+
},
|
|
67
|
+
);
|
|
68
|
+
} else {
|
|
69
|
+
logger.error('Unknown server method: ' + name);
|
|
70
|
+
captureException(new Error('Unknown server method: ' + name));
|
|
71
|
+
const unknownMethodError = APIError('Unknown server method: ' + name);
|
|
72
|
+
|
|
73
|
+
if (catchErrors) {
|
|
74
|
+
process.parentPort.postMessage({
|
|
75
|
+
type: 'reply',
|
|
76
|
+
id,
|
|
77
|
+
result: catchErrors
|
|
78
|
+
? { error: unknownMethodError, data: null }
|
|
79
|
+
: null,
|
|
80
|
+
});
|
|
81
|
+
} else {
|
|
82
|
+
process.parentPort.postMessage({
|
|
83
|
+
type: 'error',
|
|
84
|
+
id,
|
|
85
|
+
error: unknownMethodError,
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
});
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
export const getNumClients: T.GetNumClients = function () {
|
|
93
|
+
return 0;
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
export const send: T.Send = function (name, args) {
|
|
97
|
+
process.parentPort.postMessage({ type: 'push', name, args });
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
export const resetEvents: T.ResetEvents = function () {
|
|
101
|
+
// resetEvents is used in tests to mock the server
|
|
102
|
+
};
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
// @ts-strict-ignore
|
|
2
|
+
import { APIError } from '../../../server/errors';
|
|
3
|
+
import { isMutating, runHandler } from '../../../server/mutators';
|
|
4
|
+
import { captureException } from '../../exceptions';
|
|
5
|
+
import { logger } from '../log';
|
|
6
|
+
|
|
7
|
+
import type * as T from './index-types';
|
|
8
|
+
|
|
9
|
+
function getGlobalObject() {
|
|
10
|
+
const obj =
|
|
11
|
+
typeof window !== 'undefined'
|
|
12
|
+
? window
|
|
13
|
+
: typeof self !== 'undefined'
|
|
14
|
+
? self
|
|
15
|
+
: null;
|
|
16
|
+
if (!obj) {
|
|
17
|
+
throw new Error('Cannot get global object');
|
|
18
|
+
}
|
|
19
|
+
return obj as unknown as typeof globalThis & {
|
|
20
|
+
__globalServerChannel: Window | null;
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
getGlobalObject().__globalServerChannel = null;
|
|
25
|
+
|
|
26
|
+
function coerceError(error) {
|
|
27
|
+
if (error.type && error.type === 'APIError') {
|
|
28
|
+
return error;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
return { type: 'ServerError', message: error.message, cause: error };
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export const init: T.Init = function (serverChn, handlers) {
|
|
35
|
+
const serverChannel = serverChn as Window;
|
|
36
|
+
getGlobalObject().__globalServerChannel = serverChannel;
|
|
37
|
+
|
|
38
|
+
serverChannel.addEventListener(
|
|
39
|
+
'message',
|
|
40
|
+
e => {
|
|
41
|
+
const data = e.data;
|
|
42
|
+
const msg = typeof data === 'string' ? JSON.parse(data) : data;
|
|
43
|
+
|
|
44
|
+
if (msg.type && (msg.type === 'init' || msg.type.startsWith('__'))) {
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
if (msg.name === 'client-connected-to-backend') {
|
|
49
|
+
// the client is indicating that it is connected to this backend. Stop attempting to connect
|
|
50
|
+
logger.info('Backend: Client connected');
|
|
51
|
+
clearInterval(reconnectToClientInterval);
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const { id, name, args, undoTag, catchErrors } = msg;
|
|
56
|
+
|
|
57
|
+
if (handlers[name]) {
|
|
58
|
+
runHandler(handlers[name], args, { undoTag, name }).then(
|
|
59
|
+
result => {
|
|
60
|
+
serverChannel.postMessage({
|
|
61
|
+
type: 'reply',
|
|
62
|
+
id,
|
|
63
|
+
result: catchErrors ? { data: result, error: null } : result,
|
|
64
|
+
mutated: isMutating(handlers[name]),
|
|
65
|
+
undoTag,
|
|
66
|
+
});
|
|
67
|
+
},
|
|
68
|
+
nativeError => {
|
|
69
|
+
const error = coerceError(nativeError);
|
|
70
|
+
|
|
71
|
+
if (name.startsWith('api/')) {
|
|
72
|
+
// The API is newer and does automatically forward
|
|
73
|
+
// errors
|
|
74
|
+
serverChannel.postMessage({ type: 'reply', id, error });
|
|
75
|
+
} else if (catchErrors) {
|
|
76
|
+
serverChannel.postMessage({
|
|
77
|
+
type: 'reply',
|
|
78
|
+
id,
|
|
79
|
+
result: { error, data: null },
|
|
80
|
+
});
|
|
81
|
+
} else {
|
|
82
|
+
serverChannel.postMessage({ type: 'error', id, error });
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// Only report internal errors
|
|
86
|
+
if (error.type === 'ServerError') {
|
|
87
|
+
captureException(nativeError);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
if (!catchErrors) {
|
|
91
|
+
// Notify the frontend that something bad happend
|
|
92
|
+
send('server-error');
|
|
93
|
+
}
|
|
94
|
+
},
|
|
95
|
+
);
|
|
96
|
+
} else {
|
|
97
|
+
logger.error('Unknown server method: ' + name);
|
|
98
|
+
captureException(new Error('Unknown server method: ' + name));
|
|
99
|
+
const unknownMethodError = APIError('Unknown server method: ' + name);
|
|
100
|
+
|
|
101
|
+
if (catchErrors) {
|
|
102
|
+
serverChannel.postMessage({
|
|
103
|
+
type: 'reply',
|
|
104
|
+
id,
|
|
105
|
+
result: catchErrors
|
|
106
|
+
? { error: unknownMethodError, data: null }
|
|
107
|
+
: null,
|
|
108
|
+
});
|
|
109
|
+
} else {
|
|
110
|
+
serverChannel.postMessage({
|
|
111
|
+
type: 'error',
|
|
112
|
+
id,
|
|
113
|
+
error: unknownMethodError,
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
},
|
|
118
|
+
false,
|
|
119
|
+
);
|
|
120
|
+
|
|
121
|
+
const RECONNECT_INTERVAL_MS = 200;
|
|
122
|
+
const MAX_RECONNECT_ATTEMPTS = 500;
|
|
123
|
+
let reconnectAttempts = 0;
|
|
124
|
+
|
|
125
|
+
const reconnectToClientInterval = setInterval(() => {
|
|
126
|
+
logger.info('Backend: Trying to connect to client');
|
|
127
|
+
serverChannel.postMessage({ type: 'connect' });
|
|
128
|
+
reconnectAttempts++;
|
|
129
|
+
if (reconnectAttempts >= MAX_RECONNECT_ATTEMPTS) {
|
|
130
|
+
// Failed to connect to client - signal server error
|
|
131
|
+
send('server-error');
|
|
132
|
+
clearInterval(reconnectToClientInterval);
|
|
133
|
+
}
|
|
134
|
+
}, RECONNECT_INTERVAL_MS);
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
export const send: T.Send = function (name, args) {
|
|
138
|
+
const { __globalServerChannel } = getGlobalObject();
|
|
139
|
+
if (__globalServerChannel) {
|
|
140
|
+
__globalServerChannel.postMessage({
|
|
141
|
+
type: 'push',
|
|
142
|
+
name,
|
|
143
|
+
args,
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
};
|
|
147
|
+
|
|
148
|
+
export const getNumClients = function () {
|
|
149
|
+
return 1;
|
|
150
|
+
};
|
|
151
|
+
|
|
152
|
+
export const resetEvents: T.ResetEvents = function () {
|
|
153
|
+
// resetEvents is used in tests to mock the server
|
|
154
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const fetch = globalThis.fetch;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { logger } from '../log';
|
|
2
|
+
|
|
3
|
+
import type * as T from './index';
|
|
4
|
+
|
|
5
|
+
export const fetch: typeof T.fetch = async (input, options) => {
|
|
6
|
+
try {
|
|
7
|
+
return await globalThis.fetch(input, {
|
|
8
|
+
...options,
|
|
9
|
+
headers: {
|
|
10
|
+
...options?.headers,
|
|
11
|
+
origin: 'app://actual',
|
|
12
|
+
},
|
|
13
|
+
});
|
|
14
|
+
} catch (error) {
|
|
15
|
+
logger.error(error); // log error
|
|
16
|
+
throw error;
|
|
17
|
+
}
|
|
18
|
+
};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import * as connection from '../connection';
|
|
2
|
+
|
|
3
|
+
export const fetch = async (
|
|
4
|
+
input: RequestInfo | URL,
|
|
5
|
+
options: RequestInit = {},
|
|
6
|
+
): Promise<Response> => {
|
|
7
|
+
// Set redirect to manual so that we can detect and respond to redirects.
|
|
8
|
+
if (!options.redirect) options.redirect = 'manual';
|
|
9
|
+
|
|
10
|
+
const response = await globalThis.fetch(input, options);
|
|
11
|
+
|
|
12
|
+
// Authentication proxies redirect when authentication has expired. In this case,
|
|
13
|
+
// we want to fully reload and yeild control from the service worker back to the server.
|
|
14
|
+
if (response.type === 'opaqueredirect') {
|
|
15
|
+
connection.send('api-fetch-redirected');
|
|
16
|
+
throw new Error(`API request redirected`);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
return response;
|
|
20
|
+
};
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
// @ts-strict-ignore
|
|
2
|
+
import * as fs from 'fs';
|
|
3
|
+
import * as path from 'path';
|
|
4
|
+
|
|
5
|
+
import promiseRetry from 'promise-retry';
|
|
6
|
+
|
|
7
|
+
import { logger } from '../log';
|
|
8
|
+
|
|
9
|
+
import type * as T from './index';
|
|
10
|
+
|
|
11
|
+
export { getDocumentDir, getBudgetDir, _setDocumentDir } from './shared';
|
|
12
|
+
|
|
13
|
+
export const init: typeof T.init = async () => {
|
|
14
|
+
// Nothing to do
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
export const getDataDir: typeof T.getDataDir = () => {
|
|
18
|
+
if (!process.env.ACTUAL_DATA_DIR) {
|
|
19
|
+
throw new Error('ACTUAL_DATA_DIR env variable is required');
|
|
20
|
+
}
|
|
21
|
+
return process.env.ACTUAL_DATA_DIR;
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
export const bundledDatabasePath: typeof T.bundledDatabasePath = path.join(
|
|
25
|
+
__dirname,
|
|
26
|
+
'default-db.sqlite',
|
|
27
|
+
);
|
|
28
|
+
|
|
29
|
+
export const migrationsPath: typeof T.migrationsPath = path.join(
|
|
30
|
+
__dirname,
|
|
31
|
+
'migrations',
|
|
32
|
+
);
|
|
33
|
+
|
|
34
|
+
export const demoBudgetPath: typeof T.demoBudgetPath = path.join(
|
|
35
|
+
__dirname,
|
|
36
|
+
'demo-budget',
|
|
37
|
+
);
|
|
38
|
+
|
|
39
|
+
export const join: typeof T.join = (...args: Parameters<typeof path.join>) =>
|
|
40
|
+
path.join(...args);
|
|
41
|
+
|
|
42
|
+
export const basename: typeof T.basename = filepath => path.basename(filepath);
|
|
43
|
+
|
|
44
|
+
export const listDir: typeof T.listDir = filepath =>
|
|
45
|
+
new Promise((resolve, reject) => {
|
|
46
|
+
fs.readdir(filepath, (err, files) => {
|
|
47
|
+
if (err) {
|
|
48
|
+
reject(err);
|
|
49
|
+
} else {
|
|
50
|
+
resolve(files);
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
export const exists: typeof T.exists = filepath =>
|
|
56
|
+
new Promise(resolve => {
|
|
57
|
+
fs.access(filepath, fs.constants.F_OK, err => {
|
|
58
|
+
return resolve(!err);
|
|
59
|
+
});
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
export const mkdir: typeof T.mkdir = filepath =>
|
|
63
|
+
new Promise((resolve, reject) => {
|
|
64
|
+
fs.mkdir(filepath, err => {
|
|
65
|
+
if (err) {
|
|
66
|
+
reject(err);
|
|
67
|
+
} else {
|
|
68
|
+
resolve(undefined);
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
export const size: typeof T.size = filepath =>
|
|
74
|
+
new Promise((resolve, reject) => {
|
|
75
|
+
fs.stat(filepath, (err, stats) => {
|
|
76
|
+
if (err) {
|
|
77
|
+
reject(err);
|
|
78
|
+
} else {
|
|
79
|
+
resolve(stats.size);
|
|
80
|
+
}
|
|
81
|
+
});
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
export const copyFile: typeof T.copyFile = (frompath, topath) => {
|
|
85
|
+
return new Promise<boolean>((resolve, reject) => {
|
|
86
|
+
const readStream = fs.createReadStream(frompath);
|
|
87
|
+
const writeStream = fs.createWriteStream(topath);
|
|
88
|
+
|
|
89
|
+
readStream.on('error', reject);
|
|
90
|
+
writeStream.on('error', reject);
|
|
91
|
+
|
|
92
|
+
writeStream.on('open', () => readStream.pipe(writeStream));
|
|
93
|
+
writeStream.once('close', () => resolve(true));
|
|
94
|
+
});
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
export const readFile: typeof T.readFile = (
|
|
98
|
+
filepath: string,
|
|
99
|
+
encoding: 'utf8' | 'binary' | null = 'utf8',
|
|
100
|
+
) => {
|
|
101
|
+
if (encoding === 'binary') {
|
|
102
|
+
// `binary` is not actually a valid encoding, you pass `null` into node if
|
|
103
|
+
// you want a buffer
|
|
104
|
+
encoding = null;
|
|
105
|
+
}
|
|
106
|
+
// `any` as cannot refine return with two function overrides
|
|
107
|
+
// oxlint-disable-next-line typescript/no-explicit-any
|
|
108
|
+
return new Promise<any>((resolve, reject) => {
|
|
109
|
+
fs.readFile(filepath, encoding, (err, data) => {
|
|
110
|
+
if (err) {
|
|
111
|
+
reject(err);
|
|
112
|
+
} else {
|
|
113
|
+
resolve(data);
|
|
114
|
+
}
|
|
115
|
+
});
|
|
116
|
+
});
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
export const writeFile: typeof T.writeFile = async (filepath, contents) => {
|
|
120
|
+
try {
|
|
121
|
+
await promiseRetry(
|
|
122
|
+
(retry, attempt) => {
|
|
123
|
+
return new Promise((resolve, reject) => {
|
|
124
|
+
fs.writeFile(filepath, contents, 'utf8', err => {
|
|
125
|
+
if (err) {
|
|
126
|
+
logger.error(
|
|
127
|
+
`Failed to write to ${filepath}. Attempted ${attempt} times. Something is locking the file - potentially a virus scanner or backup software.`,
|
|
128
|
+
);
|
|
129
|
+
reject(err);
|
|
130
|
+
} else {
|
|
131
|
+
if (attempt > 1) {
|
|
132
|
+
logger.info(
|
|
133
|
+
`Successfully recovered from file lock. It took ${attempt} retries`,
|
|
134
|
+
);
|
|
135
|
+
}
|
|
136
|
+
resolve(undefined);
|
|
137
|
+
}
|
|
138
|
+
});
|
|
139
|
+
}).catch(retry);
|
|
140
|
+
},
|
|
141
|
+
{
|
|
142
|
+
retries: 20,
|
|
143
|
+
minTimeout: 100,
|
|
144
|
+
maxTimeout: 500,
|
|
145
|
+
factor: 1.5,
|
|
146
|
+
},
|
|
147
|
+
);
|
|
148
|
+
|
|
149
|
+
return undefined;
|
|
150
|
+
} catch (err) {
|
|
151
|
+
logger.error(`Unable to recover from file lock on file ${filepath}`);
|
|
152
|
+
throw err;
|
|
153
|
+
}
|
|
154
|
+
};
|
|
155
|
+
|
|
156
|
+
export const removeFile: typeof T.removeFile = filepath => {
|
|
157
|
+
return new Promise(function (resolve, reject) {
|
|
158
|
+
fs.unlink(filepath, err => {
|
|
159
|
+
return err ? reject(err) : resolve(undefined);
|
|
160
|
+
});
|
|
161
|
+
});
|
|
162
|
+
};
|
|
163
|
+
|
|
164
|
+
export const removeDir: typeof T.removeDir = dirpath => {
|
|
165
|
+
return new Promise(function (resolve, reject) {
|
|
166
|
+
fs.rmdir(dirpath, err => {
|
|
167
|
+
return err ? reject(err) : resolve(undefined);
|
|
168
|
+
});
|
|
169
|
+
});
|
|
170
|
+
};
|
|
171
|
+
|
|
172
|
+
export const removeDirRecursively: typeof T.removeDirRecursively =
|
|
173
|
+
async dirpath => {
|
|
174
|
+
if (await exists(dirpath)) {
|
|
175
|
+
for (const file of await listDir(dirpath)) {
|
|
176
|
+
const fullpath = join(dirpath, file);
|
|
177
|
+
if (fs.statSync(fullpath).isDirectory()) {
|
|
178
|
+
await removeDirRecursively(fullpath);
|
|
179
|
+
} else {
|
|
180
|
+
await removeFile(fullpath);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
await removeDir(dirpath);
|
|
185
|
+
}
|
|
186
|
+
};
|
|
187
|
+
|
|
188
|
+
export const getModifiedTime: typeof T.getModifiedTime = filepath => {
|
|
189
|
+
return new Promise(function (resolve, reject) {
|
|
190
|
+
fs.stat(filepath, (err, stats) => {
|
|
191
|
+
if (err) {
|
|
192
|
+
reject(err);
|
|
193
|
+
} else {
|
|
194
|
+
resolve(new Date(stats.mtime));
|
|
195
|
+
}
|
|
196
|
+
});
|
|
197
|
+
});
|
|
198
|
+
};
|