@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,485 @@
|
|
|
1
|
+
// @ts-strict-ignore
|
|
2
|
+
import * as d from 'date-fns';
|
|
3
|
+
import type { Locale } from 'date-fns';
|
|
4
|
+
import memoizeOne from 'memoize-one';
|
|
5
|
+
|
|
6
|
+
import type { SyncedPrefs } from '../types/prefs';
|
|
7
|
+
|
|
8
|
+
import * as Platform from './platform';
|
|
9
|
+
|
|
10
|
+
type DateLike = string | Date;
|
|
11
|
+
type Day = 0 | 1 | 2 | 3 | 4 | 5 | 6;
|
|
12
|
+
|
|
13
|
+
export function _parse(value: DateLike): Date {
|
|
14
|
+
if (typeof value === 'string') {
|
|
15
|
+
// Dates are hard. We just want to deal with months in the format
|
|
16
|
+
// 2020-01 and days in the format 2020-01-01, but life is never
|
|
17
|
+
// simple. We want to rely on native dates for date logic because
|
|
18
|
+
// days are complicated (leap years, etc). But relying on native
|
|
19
|
+
// dates mean we're exposed to craziness.
|
|
20
|
+
//
|
|
21
|
+
// The biggest problem is that JS dates work with local time by
|
|
22
|
+
// default. We could try to only work with UTC, but there's not an
|
|
23
|
+
// easy way to make `format` avoid local time, and not sure if we
|
|
24
|
+
// want that anyway (`currentMonth` should surely print the local
|
|
25
|
+
// time). We need to embrace local time, and as long as inputs to
|
|
26
|
+
// date logic and outputs from format are local time, it should
|
|
27
|
+
// work.
|
|
28
|
+
//
|
|
29
|
+
// To make sure we're in local time, always give Date integer
|
|
30
|
+
// values. If you pass in a string to parse, different string
|
|
31
|
+
// formats produce different results.
|
|
32
|
+
//
|
|
33
|
+
// A big problem is daylight savings, however. Usually, when
|
|
34
|
+
// giving the time to the Date constructor, you get back a date
|
|
35
|
+
// specifically for that time in your local timezone. However, if
|
|
36
|
+
// daylight savings occurs on that exact time, you will get back
|
|
37
|
+
// something different:
|
|
38
|
+
//
|
|
39
|
+
// This is fine:
|
|
40
|
+
// > new Date(2017, 2, 12, 1).toString()
|
|
41
|
+
// > 'Sun Mar 12 2017 01:00:00 GMT-0500 (Eastern Standard Time)'
|
|
42
|
+
//
|
|
43
|
+
// But wait, we got back a different time (3AM instead of 2AM):
|
|
44
|
+
// > new Date(2017, 2, 12, 2).toString()
|
|
45
|
+
// > 'Sun Mar 12 2017 03:00:00 GMT-0400 (Eastern Daylight Time)'
|
|
46
|
+
//
|
|
47
|
+
// The time is "correctly" adjusted via DST, but we _really_
|
|
48
|
+
// wanted 2AM. The problem is that time simply doesn't exist.
|
|
49
|
+
//
|
|
50
|
+
// Why is this a problem? Well, consider a case where the DST
|
|
51
|
+
// shift happens *at midnight* and it goes back an hour. You think
|
|
52
|
+
// you have a date object for the next day, but when formatted it
|
|
53
|
+
// actually shows the previous day. A more likely scenario: buggy
|
|
54
|
+
// timezone data makes JS dates do this shift when it shouldn't,
|
|
55
|
+
// so using midnight at the time for date logic gives back the
|
|
56
|
+
// last day. See the time range of Sep 30 15:00 - Oct 1 1:00 for
|
|
57
|
+
// the AEST timezone when nodejs-mobile incorrectly gives you back
|
|
58
|
+
// a time an hour *before* you specified. Since this happens on
|
|
59
|
+
// Oct 1, doing `addMonths(September, 1)` still gives you back
|
|
60
|
+
// September. Issue here:
|
|
61
|
+
// https://github.com/JaneaSystems/nodejs-mobile/issues/251
|
|
62
|
+
//
|
|
63
|
+
// The fix is simple once you understand this. Always use the 12th
|
|
64
|
+
// hour of the day. That's it. There is no DST that shifts more
|
|
65
|
+
// than 12 hours (god let's hope not) so no matter how far DST has
|
|
66
|
+
// shifted backwards or forwards, doing date logic will stay
|
|
67
|
+
// within the day we want.
|
|
68
|
+
|
|
69
|
+
const [year, month, day] = value.split('-');
|
|
70
|
+
if (day != null) {
|
|
71
|
+
return new Date(parseInt(year), parseInt(month) - 1, parseInt(day), 12);
|
|
72
|
+
} else if (month != null) {
|
|
73
|
+
return new Date(parseInt(year), parseInt(month) - 1, 1, 12);
|
|
74
|
+
} else {
|
|
75
|
+
return new Date(parseInt(year), 0, 1, 12);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
if (typeof value === 'number') {
|
|
79
|
+
return new Date(value);
|
|
80
|
+
}
|
|
81
|
+
return value;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export const parseDate = _parse;
|
|
85
|
+
|
|
86
|
+
export function yearFromDate(date: DateLike): string {
|
|
87
|
+
return d.format(_parse(date), 'yyyy');
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export function monthFromDate(date: DateLike): string {
|
|
91
|
+
return d.format(_parse(date), 'yyyy-MM');
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
export function weekFromDate(
|
|
95
|
+
date: DateLike,
|
|
96
|
+
firstDayOfWeekIdx: SyncedPrefs['firstDayOfWeekIdx'],
|
|
97
|
+
): string {
|
|
98
|
+
const converted = parseInt(firstDayOfWeekIdx || '0') as Day;
|
|
99
|
+
return d.format(
|
|
100
|
+
_parse(d.startOfWeek(_parse(date), { weekStartsOn: converted })),
|
|
101
|
+
'yyyy-MM-dd',
|
|
102
|
+
);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export function firstDayOfMonth(date: DateLike): string {
|
|
106
|
+
return dayFromDate(d.startOfMonth(_parse(date)));
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
export function lastDayOfMonth(date: DateLike): string {
|
|
110
|
+
return dayFromDate(d.endOfMonth(_parse(date)));
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
export function dayFromDate(date: DateLike): string {
|
|
114
|
+
return d.format(_parse(date), 'yyyy-MM-dd');
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
export function currentMonth(): string {
|
|
118
|
+
if (global.IS_TESTING || Platform.isPlaywright) {
|
|
119
|
+
return global.currentMonth || '2017-01';
|
|
120
|
+
} else {
|
|
121
|
+
return d.format(new Date(), 'yyyy-MM');
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
export function currentWeek(
|
|
126
|
+
firstDayOfWeekIdx?: SyncedPrefs['firstDayOfWeekIdx'],
|
|
127
|
+
): string {
|
|
128
|
+
if (global.IS_TESTING || Platform.isPlaywright) {
|
|
129
|
+
return global.currentWeek || '2017-01-01';
|
|
130
|
+
} else {
|
|
131
|
+
const converted = parseInt(firstDayOfWeekIdx || '0') as Day;
|
|
132
|
+
return d.format(
|
|
133
|
+
_parse(d.startOfWeek(new Date(), { weekStartsOn: converted })),
|
|
134
|
+
'yyyy-MM-dd',
|
|
135
|
+
);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
export function currentYear(): string {
|
|
140
|
+
if (global.IS_TESTING || Platform.isPlaywright) {
|
|
141
|
+
return global.currentMonth || '2017';
|
|
142
|
+
} else {
|
|
143
|
+
return d.format(new Date(), 'yyyy');
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
export function currentDate(): Date {
|
|
148
|
+
if (global.IS_TESTING || Platform.isPlaywright) {
|
|
149
|
+
return d.parse(currentDay(), 'yyyy-MM-dd', new Date());
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
return new Date();
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
export function currentDay(): string {
|
|
156
|
+
if (global.IS_TESTING || Platform.isPlaywright) {
|
|
157
|
+
return '2017-01-01';
|
|
158
|
+
} else {
|
|
159
|
+
return d.format(new Date(), 'yyyy-MM-dd');
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
export function nextMonth(month: DateLike): string {
|
|
164
|
+
return d.format(d.addMonths(_parse(month), 1), 'yyyy-MM');
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
export function prevYear(month: DateLike, format = 'yyyy-MM'): string {
|
|
168
|
+
return d.format(d.subMonths(_parse(month), 12), format);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
export function prevMonth(month: DateLike): string {
|
|
172
|
+
return d.format(d.subMonths(_parse(month), 1), 'yyyy-MM');
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
export function addYears(year: DateLike, n: number): string {
|
|
176
|
+
return d.format(d.addYears(_parse(year), n), 'yyyy');
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
export function addMonths(month: DateLike, n: number): string {
|
|
180
|
+
return d.format(d.addMonths(_parse(month), n), 'yyyy-MM');
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
export function addWeeks(date: DateLike, n: number): string {
|
|
184
|
+
return d.format(d.addWeeks(_parse(date), n), 'yyyy-MM-dd');
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
export function differenceInCalendarMonths(
|
|
188
|
+
month1: DateLike,
|
|
189
|
+
month2: DateLike,
|
|
190
|
+
): number {
|
|
191
|
+
return d.differenceInCalendarMonths(_parse(month1), _parse(month2));
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
export function differenceInCalendarDays(
|
|
195
|
+
month1: DateLike,
|
|
196
|
+
month2: DateLike,
|
|
197
|
+
): number {
|
|
198
|
+
return d.differenceInCalendarDays(_parse(month1), _parse(month2));
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
export function subMonths(month: string | Date, n: number) {
|
|
202
|
+
return d.format(d.subMonths(_parse(month), n), 'yyyy-MM');
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
export function subWeeks(date: DateLike, n: number): string {
|
|
206
|
+
return d.format(d.subWeeks(_parse(date), n), 'yyyy-MM-dd');
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
export function subYears(year: string | Date, n: number) {
|
|
210
|
+
return d.format(d.subYears(_parse(year), n), 'yyyy');
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
export function addDays(day: DateLike, n: number): string {
|
|
214
|
+
return d.format(d.addDays(_parse(day), n), 'yyyy-MM-dd');
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
export function subDays(day: DateLike, n: number): string {
|
|
218
|
+
return d.format(d.subDays(_parse(day), n), 'yyyy-MM-dd');
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
export function isBefore(month1: DateLike, month2: DateLike): boolean {
|
|
222
|
+
return d.isBefore(_parse(month1), _parse(month2));
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
export function isAfter(month1: DateLike, month2: DateLike): boolean {
|
|
226
|
+
return d.isAfter(_parse(month1), _parse(month2));
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
export function isCurrentMonth(month: DateLike): boolean {
|
|
230
|
+
return month === currentMonth();
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
export function isCurrentDay(day: DateLike): boolean {
|
|
234
|
+
return day === currentDay();
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
// TODO: This doesn't really fit in this module anymore, should
|
|
238
|
+
// probably live elsewhere
|
|
239
|
+
export function bounds(month: DateLike): { start: number; end: number } {
|
|
240
|
+
return {
|
|
241
|
+
start: parseInt(d.format(d.startOfMonth(_parse(month)), 'yyyyMMdd')),
|
|
242
|
+
end: parseInt(d.format(d.endOfMonth(_parse(month)), 'yyyyMMdd')),
|
|
243
|
+
};
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
export function _yearRange(
|
|
247
|
+
start: DateLike,
|
|
248
|
+
end: DateLike,
|
|
249
|
+
inclusive = false,
|
|
250
|
+
): string[] {
|
|
251
|
+
const years: string[] = [];
|
|
252
|
+
let year = yearFromDate(start);
|
|
253
|
+
const endYear = yearFromDate(end);
|
|
254
|
+
while (d.isBefore(_parse(year), _parse(endYear))) {
|
|
255
|
+
years.push(year);
|
|
256
|
+
year = addYears(year, 1);
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
if (inclusive) {
|
|
260
|
+
years.push(year);
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
return years;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
export function yearRangeInclusive(start: DateLike, end: DateLike): string[] {
|
|
267
|
+
return _yearRange(start, end, true);
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
export function _weekRange(
|
|
271
|
+
start: DateLike,
|
|
272
|
+
end: DateLike,
|
|
273
|
+
inclusive = false,
|
|
274
|
+
firstDayOfWeekIdx?: SyncedPrefs['firstDayOfWeekIdx'],
|
|
275
|
+
): string[] {
|
|
276
|
+
const weeks: string[] = [];
|
|
277
|
+
let week = weekFromDate(start, firstDayOfWeekIdx);
|
|
278
|
+
const endWeek = weekFromDate(end, firstDayOfWeekIdx);
|
|
279
|
+
while (d.isBefore(_parse(week), _parse(endWeek))) {
|
|
280
|
+
weeks.push(week);
|
|
281
|
+
week = addWeeks(week, 1);
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
if (inclusive) {
|
|
285
|
+
weeks.push(week);
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
return weeks;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
export function weekRangeInclusive(
|
|
292
|
+
start: DateLike,
|
|
293
|
+
end: DateLike,
|
|
294
|
+
firstDayOfWeekIdx?: SyncedPrefs['firstDayOfWeekIdx'],
|
|
295
|
+
): string[] {
|
|
296
|
+
return _weekRange(start, end, true, firstDayOfWeekIdx);
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
export function _range(
|
|
300
|
+
start: DateLike,
|
|
301
|
+
end: DateLike,
|
|
302
|
+
inclusive = false,
|
|
303
|
+
): string[] {
|
|
304
|
+
const months: string[] = [];
|
|
305
|
+
let month = monthFromDate(start);
|
|
306
|
+
const endMonth = monthFromDate(end);
|
|
307
|
+
while (d.isBefore(_parse(month), _parse(endMonth))) {
|
|
308
|
+
months.push(month);
|
|
309
|
+
month = addMonths(month, 1);
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
if (inclusive) {
|
|
313
|
+
months.push(month);
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
return months;
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
export function range(start: DateLike, end: DateLike): string[] {
|
|
320
|
+
return _range(start, end);
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
export function rangeInclusive(start: DateLike, end: DateLike): string[] {
|
|
324
|
+
return _range(start, end, true);
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
export function _dayRange(
|
|
328
|
+
start: DateLike,
|
|
329
|
+
end: DateLike,
|
|
330
|
+
inclusive = false,
|
|
331
|
+
): string[] {
|
|
332
|
+
const days: string[] = [];
|
|
333
|
+
let day = start;
|
|
334
|
+
while (d.isBefore(_parse(day), _parse(end))) {
|
|
335
|
+
days.push(dayFromDate(day));
|
|
336
|
+
day = addDays(day, 1);
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
if (inclusive) {
|
|
340
|
+
days.push(dayFromDate(day));
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
return days;
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
export function dayRange(start: DateLike, end: DateLike) {
|
|
347
|
+
return _dayRange(start, end);
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
export function dayRangeInclusive(start: DateLike, end: DateLike) {
|
|
351
|
+
return _dayRange(start, end, true);
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
export function getMonthFromIndex(year: string, monthIndex: number) {
|
|
355
|
+
const formatMonth = `${monthIndex + 1}`.padStart(2, '0');
|
|
356
|
+
return `${year}-${formatMonth}`;
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
export function getMonthIndex(month: string): number {
|
|
360
|
+
return parseInt(month.slice(5, 7)) - 1;
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
export function getYear(month: string): string {
|
|
364
|
+
return month.slice(0, 4);
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
export function getMonth(day: string): string {
|
|
368
|
+
return day.slice(0, 7);
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
export function getDay(day: string): number {
|
|
372
|
+
return Number(d.format(_parse(day), 'dd'));
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
export function getMonthEnd(day: string): string {
|
|
376
|
+
return subDays(nextMonth(day.slice(0, 7)) + '-01', 1);
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
export function getWeekEnd(
|
|
380
|
+
date: DateLike,
|
|
381
|
+
firstDayOfWeekIdx?: SyncedPrefs['firstDayOfWeekIdx'],
|
|
382
|
+
): string {
|
|
383
|
+
const converted = parseInt(firstDayOfWeekIdx || '0') as Day;
|
|
384
|
+
return d.format(
|
|
385
|
+
_parse(d.endOfWeek(_parse(date), { weekStartsOn: converted })),
|
|
386
|
+
'yyyy-MM-dd',
|
|
387
|
+
);
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
export function getYearStart(month: string): string {
|
|
391
|
+
return getYear(month) + '-01';
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
export function getYearEnd(month: string): string {
|
|
395
|
+
return getYear(month) + '-12';
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
export function sheetForMonth(month: string): string {
|
|
399
|
+
return 'budget' + month.replace('-', '');
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
export function nameForMonth(month: DateLike, locale?: Locale): string {
|
|
403
|
+
return d.format(_parse(month), "MMMM ''yy", { locale });
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
export function format(
|
|
407
|
+
month: DateLike,
|
|
408
|
+
format: string,
|
|
409
|
+
locale?: Locale,
|
|
410
|
+
): string {
|
|
411
|
+
return d.format(_parse(month), format, { locale });
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
export function formatDistance(
|
|
415
|
+
date1: DateLike,
|
|
416
|
+
date2: DateLike,
|
|
417
|
+
locale?: Locale,
|
|
418
|
+
options?: { addSuffix?: boolean; includeSeconds?: boolean },
|
|
419
|
+
): string {
|
|
420
|
+
return d.formatDistance(_parse(date1), _parse(date2), {
|
|
421
|
+
locale,
|
|
422
|
+
...options,
|
|
423
|
+
});
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
export const getDateFormatRegex = memoizeOne((format: string) => {
|
|
427
|
+
return new RegExp(
|
|
428
|
+
format
|
|
429
|
+
.replace(/d+/g, '\\d{1,2}')
|
|
430
|
+
.replace(/M+/g, '\\d{1,2}')
|
|
431
|
+
.replace(/y+/g, '\\d{4}'),
|
|
432
|
+
);
|
|
433
|
+
});
|
|
434
|
+
|
|
435
|
+
export const getDayMonthFormat = memoizeOne((format: string) => {
|
|
436
|
+
return format
|
|
437
|
+
.replace(/y+/g, '')
|
|
438
|
+
.replace(/[^\w]$/, '')
|
|
439
|
+
.replace(/^[^\w]/, '');
|
|
440
|
+
});
|
|
441
|
+
|
|
442
|
+
export const getDayMonthRegex = memoizeOne((format: string) => {
|
|
443
|
+
const regex = format
|
|
444
|
+
.replace(/y+/g, '')
|
|
445
|
+
.replace(/[^\w]$/, '')
|
|
446
|
+
.replace(/^[^\w]/, '')
|
|
447
|
+
.replace(/d+/g, '\\d{1,2}')
|
|
448
|
+
.replace(/M+/g, '\\d{1,2}');
|
|
449
|
+
return new RegExp('^' + regex + '$');
|
|
450
|
+
});
|
|
451
|
+
|
|
452
|
+
export const getMonthYearFormat = memoizeOne((format: string) => {
|
|
453
|
+
return format
|
|
454
|
+
.replace(/d+/g, '')
|
|
455
|
+
.replace(/[^\w]$/, '')
|
|
456
|
+
.replace(/^[^\w]/, '')
|
|
457
|
+
.replace(/\/\//, '/')
|
|
458
|
+
.replace(/\.\./, '.')
|
|
459
|
+
.replace(/--/, '-');
|
|
460
|
+
});
|
|
461
|
+
|
|
462
|
+
export const getMonthYearRegex = memoizeOne((format: string) => {
|
|
463
|
+
const regex = format
|
|
464
|
+
.replace(/d+/g, '')
|
|
465
|
+
.replace(/[^\w]$/, '')
|
|
466
|
+
.replace(/^[^\w]/, '')
|
|
467
|
+
.replace(/\/\//, '/')
|
|
468
|
+
.replace(/M+/g, '\\d{1,2}')
|
|
469
|
+
.replace(/y+/g, '\\d{2,4}');
|
|
470
|
+
return new RegExp('^' + regex + '$');
|
|
471
|
+
});
|
|
472
|
+
|
|
473
|
+
export const getShortYearFormat = memoizeOne((format: string) => {
|
|
474
|
+
return format.replace(/y+/g, 'yy');
|
|
475
|
+
});
|
|
476
|
+
|
|
477
|
+
export const getShortYearRegex = memoizeOne((format: string) => {
|
|
478
|
+
const regex = format
|
|
479
|
+
.replace(/[^\w]$/, '')
|
|
480
|
+
.replace(/^[^\w]/, '')
|
|
481
|
+
.replace(/d+/g, '\\d{1,2}')
|
|
482
|
+
.replace(/M+/g, '\\d{1,2}')
|
|
483
|
+
.replace(/y+/g, '\\d{2}');
|
|
484
|
+
return new RegExp('^' + regex + '$');
|
|
485
|
+
});
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import os from 'os';
|
|
2
|
+
|
|
3
|
+
import type * as T from './platform';
|
|
4
|
+
|
|
5
|
+
const isWindows = os.platform() === 'win32';
|
|
6
|
+
const isMac = os.platform() === 'darwin';
|
|
7
|
+
const isLinux = os.platform() === 'linux';
|
|
8
|
+
|
|
9
|
+
export const isPlaywright = false;
|
|
10
|
+
|
|
11
|
+
export const OS: typeof T.OS = isWindows
|
|
12
|
+
? 'windows'
|
|
13
|
+
: isMac
|
|
14
|
+
? 'mac'
|
|
15
|
+
: isLinux
|
|
16
|
+
? 'linux'
|
|
17
|
+
: 'unknown';
|
|
18
|
+
export const env: typeof T.env = 'unknown';
|
|
19
|
+
export const isBrowser: typeof T.isBrowser = false;
|
|
20
|
+
|
|
21
|
+
export const isIOSAgent: typeof T.isIOSAgent = false;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { UAParser } from 'ua-parser-js';
|
|
2
|
+
|
|
3
|
+
const isWindows =
|
|
4
|
+
navigator.platform && navigator.platform.toLowerCase() === 'win32';
|
|
5
|
+
|
|
6
|
+
const isMac =
|
|
7
|
+
navigator.platform && navigator.platform.toUpperCase().indexOf('MAC') >= 0;
|
|
8
|
+
|
|
9
|
+
export const isPlaywright = navigator.userAgent === 'playwright';
|
|
10
|
+
|
|
11
|
+
export const OS: 'windows' | 'mac' | 'linux' | 'unknown' = isWindows
|
|
12
|
+
? 'windows'
|
|
13
|
+
: isMac
|
|
14
|
+
? 'mac'
|
|
15
|
+
: 'linux';
|
|
16
|
+
export const env: 'web' | 'mobile' | 'unknown' = 'web';
|
|
17
|
+
export const isBrowser: boolean = true;
|
|
18
|
+
|
|
19
|
+
const agent = UAParser(navigator.userAgent);
|
|
20
|
+
export const isIOSAgent = agent.browser.name === 'Mobile Safari';
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
import type { WithRequired } from '../types/util';
|
|
2
|
+
|
|
3
|
+
type ObjectExpression = {
|
|
4
|
+
[key: string]: ObjectExpression | unknown;
|
|
5
|
+
};
|
|
6
|
+
|
|
7
|
+
export type QueryState = {
|
|
8
|
+
get table(): string;
|
|
9
|
+
get tableOptions(): Readonly<Record<string, unknown>>;
|
|
10
|
+
get filterExpressions(): ReadonlyArray<ObjectExpression>;
|
|
11
|
+
get selectExpressions(): ReadonlyArray<ObjectExpression | string | '*'>;
|
|
12
|
+
get groupExpressions(): ReadonlyArray<ObjectExpression | string>;
|
|
13
|
+
get orderExpressions(): ReadonlyArray<ObjectExpression | string>;
|
|
14
|
+
get calculation(): boolean;
|
|
15
|
+
get rawMode(): boolean;
|
|
16
|
+
get withDead(): boolean;
|
|
17
|
+
get validateRefs(): boolean;
|
|
18
|
+
get limit(): number | null;
|
|
19
|
+
get offset(): number | null;
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
export class Query {
|
|
23
|
+
state: QueryState;
|
|
24
|
+
|
|
25
|
+
constructor(state: WithRequired<Partial<QueryState>, 'table'>) {
|
|
26
|
+
this.state = {
|
|
27
|
+
tableOptions: state.tableOptions || {},
|
|
28
|
+
filterExpressions: state.filterExpressions || [],
|
|
29
|
+
selectExpressions: state.selectExpressions || [],
|
|
30
|
+
groupExpressions: state.groupExpressions || [],
|
|
31
|
+
orderExpressions: state.orderExpressions || [],
|
|
32
|
+
calculation: false,
|
|
33
|
+
rawMode: false,
|
|
34
|
+
withDead: false,
|
|
35
|
+
validateRefs: true,
|
|
36
|
+
limit: null,
|
|
37
|
+
offset: null,
|
|
38
|
+
...state,
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
filter(expr: ObjectExpression) {
|
|
43
|
+
return new Query({
|
|
44
|
+
...this.state,
|
|
45
|
+
filterExpressions: [...this.state.filterExpressions, expr],
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
unfilter(exprs?: Array<keyof ObjectExpression>) {
|
|
50
|
+
// Remove all filters if no arguments are passed
|
|
51
|
+
if (!exprs) {
|
|
52
|
+
return new Query({
|
|
53
|
+
...this.state,
|
|
54
|
+
filterExpressions: [],
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const exprSet = new Set(exprs);
|
|
59
|
+
return new Query({
|
|
60
|
+
...this.state,
|
|
61
|
+
filterExpressions: this.state.filterExpressions.filter(
|
|
62
|
+
expr => !exprSet.has(Object.keys(expr)[0]),
|
|
63
|
+
),
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
select(
|
|
68
|
+
exprs:
|
|
69
|
+
| Array<ObjectExpression | string>
|
|
70
|
+
| ObjectExpression
|
|
71
|
+
| string
|
|
72
|
+
| '*'
|
|
73
|
+
| ['*'] = [],
|
|
74
|
+
) {
|
|
75
|
+
if (!Array.isArray(exprs)) {
|
|
76
|
+
exprs = [exprs];
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
return new Query({
|
|
80
|
+
...this.state,
|
|
81
|
+
selectExpressions: exprs,
|
|
82
|
+
calculation: false,
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
calculate(expr: ObjectExpression | string) {
|
|
87
|
+
return new Query({
|
|
88
|
+
...this.state,
|
|
89
|
+
selectExpressions: [{ result: expr }],
|
|
90
|
+
calculation: true,
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
groupBy(exprs: ObjectExpression | string | Array<ObjectExpression | string>) {
|
|
95
|
+
if (!Array.isArray(exprs)) {
|
|
96
|
+
exprs = [exprs];
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
return new Query({
|
|
100
|
+
...this.state,
|
|
101
|
+
groupExpressions: [...this.state.groupExpressions, ...exprs],
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
orderBy(exprs: ObjectExpression | string | Array<ObjectExpression | string>) {
|
|
106
|
+
if (!Array.isArray(exprs)) {
|
|
107
|
+
exprs = [exprs];
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
return new Query({
|
|
111
|
+
...this.state,
|
|
112
|
+
orderExpressions: [...this.state.orderExpressions, ...exprs],
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
limit(num: number) {
|
|
117
|
+
return new Query({ ...this.state, limit: num });
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
offset(num: number) {
|
|
121
|
+
return new Query({ ...this.state, offset: num });
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
raw() {
|
|
125
|
+
return new Query({ ...this.state, rawMode: true });
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
withDead() {
|
|
129
|
+
return new Query({ ...this.state, withDead: true });
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
withoutValidatedRefs() {
|
|
133
|
+
return new Query({ ...this.state, validateRefs: false });
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
options(opts: Record<string, unknown>) {
|
|
137
|
+
return new Query({ ...this.state, tableOptions: opts });
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
reset() {
|
|
141
|
+
return q(this.state.table);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
serialize() {
|
|
145
|
+
return this.state;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
serializeAsString() {
|
|
149
|
+
return JSON.stringify(this.serialize());
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
export function getPrimaryOrderBy(
|
|
154
|
+
query: Query,
|
|
155
|
+
defaultOrderBy: ObjectExpression | null,
|
|
156
|
+
) {
|
|
157
|
+
const orderExprs = query.serialize().orderExpressions;
|
|
158
|
+
if (orderExprs.length === 0) {
|
|
159
|
+
if (defaultOrderBy) {
|
|
160
|
+
return { order: 'asc', ...defaultOrderBy };
|
|
161
|
+
}
|
|
162
|
+
return null;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
const firstOrder = orderExprs[0];
|
|
166
|
+
if (typeof firstOrder === 'string') {
|
|
167
|
+
return { field: firstOrder, order: 'asc' };
|
|
168
|
+
}
|
|
169
|
+
// Handle this form: { field: 'desc' }
|
|
170
|
+
const [field] = Object.keys(firstOrder);
|
|
171
|
+
return { field, order: firstOrder[field] };
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
export function q(table: QueryState['table']) {
|
|
175
|
+
return new Query({ table });
|
|
176
|
+
}
|