@actual-app/api 26.2.0-nightly.20260113 → 26.2.0-nightly.20260114

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.
@@ -383,10 +383,24 @@ export declare const schema: {
383
383
  type: string;
384
384
  };
385
385
  };
386
+ dashboard_pages: {
387
+ id: {
388
+ type: string;
389
+ };
390
+ name: {
391
+ type: string;
392
+ };
393
+ tombstone: {
394
+ type: string;
395
+ };
396
+ };
386
397
  dashboard: {
387
398
  id: {
388
399
  type: string;
389
400
  };
401
+ dashboard_page_id: {
402
+ type: string;
403
+ };
390
404
  type: {
391
405
  type: string;
392
406
  };
@@ -6,7 +6,6 @@ export type BudgetFileHandlers = {
6
6
  'unique-budget-name': typeof handleUniqueBudgetName;
7
7
  'get-budgets': typeof getBudgets;
8
8
  'get-remote-files': typeof getRemoteFiles;
9
- 'get-user-file-info': typeof getUserFileInfo;
10
9
  'reset-budget-cache': typeof resetBudgetCache;
11
10
  'upload-budget': typeof uploadBudget;
12
11
  'download-budget': typeof downloadBudget;
@@ -35,7 +34,7 @@ export declare const app: {
35
34
  handlers: BudgetFileHandlers;
36
35
  services: (() => () => void)[];
37
36
  unlistenServices: (() => void)[];
38
- method<Name extends "load-budget" | "validate-budget-name" | "unique-budget-name" | "get-budgets" | "get-remote-files" | "get-user-file-info" | "reset-budget-cache" | "upload-budget" | "download-budget" | "sync-budget" | "create-demo-budget" | "close-budget" | "delete-budget" | "duplicate-budget" | "create-budget" | "import-budget" | "export-budget" | "upload-file-web" | "backups-get" | "backup-load" | "backup-make" | "get-last-opened-backup">(name: Name, func: BudgetFileHandlers[Name]): void;
37
+ method<Name extends "load-budget" | "validate-budget-name" | "unique-budget-name" | "get-budgets" | "get-remote-files" | "reset-budget-cache" | "upload-budget" | "download-budget" | "sync-budget" | "create-demo-budget" | "close-budget" | "delete-budget" | "duplicate-budget" | "create-budget" | "import-budget" | "export-budget" | "upload-file-web" | "backups-get" | "backup-load" | "backup-make" | "get-last-opened-backup">(name: Name, func: BudgetFileHandlers[Name]): void;
39
38
  service(func: () => () => void): void;
40
39
  combine(...apps: any[]): void;
41
40
  startServices(): void;
@@ -52,7 +51,6 @@ declare function handleUniqueBudgetName({ name }: {
52
51
  }): Promise<string>;
53
52
  declare function getBudgets(): Promise<Budget[]>;
54
53
  declare function getRemoteFiles(): Promise<cloudStorage.RemoteFile[]>;
55
- declare function getUserFileInfo(fileId: string): Promise<cloudStorage.RemoteFile>;
56
54
  declare function resetBudgetCache(): Promise<void>;
57
55
  declare function uploadBudget({ id }?: {
58
56
  id?: Budget['id'];
@@ -35,7 +35,6 @@ export declare function upload(): Promise<void>;
35
35
  export declare function possiblyUpload(): Promise<void>;
36
36
  export declare function removeFile(fileId: any): Promise<void>;
37
37
  export declare function listRemoteFiles(): Promise<RemoteFile[]>;
38
- export declare function getRemoteFile(fileId: string): Promise<RemoteFile | null>;
39
38
  export declare function download(cloudFileId: any): Promise<{
40
39
  id: any;
41
40
  }>;
@@ -1,12 +1,28 @@
1
1
  import { type Widget } from '../../types/models';
2
2
  import { type EverythingButIdOptional } from '../../types/util';
3
+ declare function getDashboardPages(): Promise<unknown[]>;
4
+ declare function createDashboardPage({ name }: {
5
+ name: string;
6
+ }): Promise<string>;
7
+ declare function deleteDashboardPage(id: string): Promise<void>;
8
+ declare function renameDashboardPage({ id, name }: {
9
+ id: string;
10
+ name: string;
11
+ }): Promise<void>;
3
12
  declare function updateDashboard(widgets: EverythingButIdOptional<Omit<Widget, 'tombstone'>>[]): Promise<void>;
4
13
  declare function updateDashboardWidget(widget: EverythingButIdOptional<Omit<Widget, 'tombstone'>>): Promise<void>;
5
- declare function resetDashboard(): Promise<void>;
6
- declare function addDashboardWidget(widget: Omit<Widget, 'id' | 'x' | 'y' | 'tombstone'> & Partial<Pick<Widget, 'x' | 'y'>>): Promise<void>;
14
+ declare function resetDashboard(id: string): Promise<void>;
15
+ declare function addDashboardWidget(widget: Omit<Widget, 'id' | 'x' | 'y' | 'tombstone'> & Partial<Pick<Widget, 'x' | 'y'>> & {
16
+ dashboard_page_id: string;
17
+ }): Promise<void>;
7
18
  declare function removeDashboardWidget(widgetId: string): Promise<void>;
8
- declare function importDashboard({ filepath }: {
19
+ declare function copyDashboardWidget({ widgetId, targetDashboardPageId, }: {
20
+ widgetId: string;
21
+ targetDashboardPageId: string;
22
+ }): Promise<void>;
23
+ declare function importDashboard({ filepath, dashboardPageId, }: {
9
24
  filepath: string;
25
+ dashboardPageId: string;
10
26
  }): Promise<{
11
27
  status: "ok";
12
28
  error?: undefined;
@@ -25,11 +41,16 @@ declare function importDashboard({ filepath }: {
25
41
  message?: undefined;
26
42
  }>;
27
43
  export type DashboardHandlers = {
44
+ 'dashboard_pages-get': typeof getDashboardPages;
45
+ 'dashboard-create': typeof createDashboardPage;
46
+ 'dashboard-delete': typeof deleteDashboardPage;
47
+ 'dashboard-rename': typeof renameDashboardPage;
28
48
  'dashboard-update': typeof updateDashboard;
29
49
  'dashboard-update-widget': typeof updateDashboardWidget;
30
50
  'dashboard-reset': typeof resetDashboard;
31
51
  'dashboard-add-widget': typeof addDashboardWidget;
32
52
  'dashboard-remove-widget': typeof removeDashboardWidget;
53
+ 'dashboard-copy-widget': typeof copyDashboardWidget;
33
54
  'dashboard-import': typeof importDashboard;
34
55
  };
35
56
  export declare const app: {
@@ -42,7 +63,7 @@ export declare const app: {
42
63
  handlers: DashboardHandlers;
43
64
  services: (() => () => void)[];
44
65
  unlistenServices: (() => void)[];
45
- method<Name extends "dashboard-update" | "dashboard-update-widget" | "dashboard-reset" | "dashboard-add-widget" | "dashboard-remove-widget" | "dashboard-import">(name: Name, func: DashboardHandlers[Name]): void;
66
+ method<Name extends "dashboard_pages-get" | "dashboard-create" | "dashboard-delete" | "dashboard-rename" | "dashboard-update" | "dashboard-update-widget" | "dashboard-reset" | "dashboard-add-widget" | "dashboard-remove-widget" | "dashboard-copy-widget" | "dashboard-import">(name: Name, func: DashboardHandlers[Name]): void;
46
67
  service(func: () => () => void): void;
47
68
  combine(...apps: any[]): void;
48
69
  startServices(): void;
@@ -193,8 +193,14 @@ export type DbCustomReport = {
193
193
  sort_by: string;
194
194
  tombstone: 1 | 0;
195
195
  };
196
+ export type DbDashboardPage = {
197
+ id: string;
198
+ name: string;
199
+ tombstone: 1 | 0;
200
+ };
196
201
  export type DbDashboard = {
197
202
  id: string;
203
+ dashboard_page_id: string;
198
204
  type: string;
199
205
  width: number;
200
206
  height: number;
@@ -9,7 +9,7 @@ export declare const app: {
9
9
  handlers: Handlers;
10
10
  services: (() => () => void)[];
11
11
  unlistenServices: (() => void)[];
12
- method<Name extends "sync" | "load-budget" | "undo" | "redo" | "make-filters-from-conditions" | "query" | "get-server-version" | "get-server-url" | "set-server-url" | "app-focused" | "api/batch-budget-start" | "api/batch-budget-end" | "api/load-budget" | "api/download-budget" | "api/get-budgets" | "api/start-import" | "api/finish-import" | "api/abort-import" | "api/query" | "api/budget-months" | "api/budget-month" | "api/budget-set-amount" | "api/budget-set-carryover" | "api/budget-hold-for-next-month" | "api/budget-reset-hold" | "api/transactions-export" | "api/transactions-import" | "api/transactions-add" | "api/transactions-get" | "api/transaction-update" | "api/transaction-delete" | "api/sync" | "api/bank-sync" | "api/accounts-get" | "api/account-create" | "api/account-update" | "api/account-close" | "api/account-reopen" | "api/account-delete" | "api/account-balance" | "api/categories-get" | "api/category-groups-get" | "api/category-group-create" | "api/category-group-update" | "api/category-group-delete" | "api/category-create" | "api/category-update" | "api/category-delete" | "api/payees-get" | "api/common-payees-get" | "api/payee-create" | "api/payee-update" | "api/payee-delete" | "api/payees-merge" | "api/rules-get" | "api/payee-rules-get" | "api/rule-create" | "api/rule-update" | "api/rule-delete" | "api/schedule-create" | "api/schedule-update" | "api/schedule-delete" | "api/schedules-get" | "api/get-id-by-name" | "api/get-server-version" | "budget/budget-amount" | "budget/copy-previous-month" | "budget/copy-single-month" | "budget/set-zero" | "budget/set-3month-avg" | "budget/set-6month-avg" | "budget/set-12month-avg" | "budget/set-n-month-avg" | "budget/check-templates" | "budget/apply-goal-template" | "budget/apply-multiple-templates" | "budget/overwrite-goal-template" | "budget/apply-single-template" | "budget/cleanup-goal-template" | "budget/hold-for-next-month" | "budget/reset-hold" | "budget/cover-overspending" | "budget/transfer-available" | "budget/cover-overbudgeted" | "budget/transfer-category" | "budget/set-carryover" | "budget/reset-income-carryover" | "get-categories" | "get-budget-bounds" | "envelope-budget-month" | "tracking-budget-month" | "category-create" | "category-update" | "category-move" | "category-delete" | "get-category-groups" | "category-group-create" | "category-group-update" | "category-group-move" | "category-group-delete" | "must-category-transfer" | "budget/get-category-automations" | "budget/set-category-automations" | "budget/store-note-templates" | "budget/render-note-templates" | "dashboard-update" | "dashboard-update-widget" | "dashboard-reset" | "dashboard-add-widget" | "dashboard-remove-widget" | "dashboard-import" | "filter-create" | "filter-update" | "filter-delete" | "notes-save" | "notes-save-undoable" | "preferences/save" | "preferences/get" | "save-global-prefs" | "load-global-prefs" | "save-prefs" | "load-prefs" | "save-server-prefs" | "report/create" | "report/update" | "report/delete" | "rule-validate" | "rule-add" | "rule-update" | "rule-delete" | "rule-delete-all" | "rule-apply-actions" | "rule-add-payee-rename" | "rules-get" | "rule-get" | "rules-run" | "schedule/create" | "schedule/update" | "schedule/delete" | "schedule/skip-next-date" | "schedule/post-transaction" | "schedule/force-run-service" | "schedule/discover" | "schedule/get-upcoming-dates" | "transactions-batch-update" | "transaction-add" | "transaction-update" | "transaction-delete" | "transactions-parse-file" | "transactions-export" | "transactions-export-query" | "transactions-merge" | "get-earliest-transaction" | "get-latest-transaction" | "users-get" | "user-delete-all" | "user-add" | "user-update" | "access-add" | "access-delete-all" | "access-get-available-users" | "transfer-ownership" | "owner-created" | "tools/fix-split-transactions" | "account-update" | "accounts-get" | "account-balance" | "account-properties" | "gocardless-accounts-link" | "simplefin-accounts-link" | "pluggyai-accounts-link" | "account-create" | "account-close" | "account-reopen" | "account-move" | "secret-set" | "secret-check" | "gocardless-poll-web-token" | "gocardless-poll-web-token-stop" | "gocardless-status" | "simplefin-status" | "pluggyai-status" | "simplefin-accounts" | "pluggyai-accounts" | "gocardless-get-banks" | "gocardless-create-web-token" | "accounts-bank-sync" | "simplefin-batch-sync" | "transactions-import" | "account-unlink" | "payee-create" | "common-payees-get" | "payees-get" | "payees-get-orphaned" | "payees-get-rule-counts" | "payees-merge" | "payees-batch-change" | "payees-check-orphaned" | "payees-get-rules" | "get-cell" | "get-cell-names" | "create-query" | "sync-reset" | "sync-repair" | "validate-budget-name" | "unique-budget-name" | "get-budgets" | "get-remote-files" | "get-user-file-info" | "reset-budget-cache" | "upload-budget" | "download-budget" | "sync-budget" | "create-demo-budget" | "close-budget" | "delete-budget" | "duplicate-budget" | "create-budget" | "import-budget" | "export-budget" | "upload-file-web" | "backups-get" | "backup-load" | "backup-make" | "get-last-opened-backup" | "key-make" | "key-test" | "tags-get" | "tags-create" | "tags-delete" | "tags-delete-all" | "tags-update" | "tags-find" | "get-did-bootstrap" | "subscribe-needs-bootstrap" | "subscribe-bootstrap" | "subscribe-get-login-methods" | "subscribe-get-user" | "subscribe-change-password" | "subscribe-sign-in" | "subscribe-sign-out" | "subscribe-set-token" | "enable-openid" | "get-openid-config" | "enable-password">(name: Name, func: Handlers[Name]): void;
12
+ method<Name extends "sync" | "load-budget" | "undo" | "redo" | "make-filters-from-conditions" | "query" | "get-server-version" | "get-server-url" | "set-server-url" | "app-focused" | "api/batch-budget-start" | "api/batch-budget-end" | "api/load-budget" | "api/download-budget" | "api/get-budgets" | "api/start-import" | "api/finish-import" | "api/abort-import" | "api/query" | "api/budget-months" | "api/budget-month" | "api/budget-set-amount" | "api/budget-set-carryover" | "api/budget-hold-for-next-month" | "api/budget-reset-hold" | "api/transactions-export" | "api/transactions-import" | "api/transactions-add" | "api/transactions-get" | "api/transaction-update" | "api/transaction-delete" | "api/sync" | "api/bank-sync" | "api/accounts-get" | "api/account-create" | "api/account-update" | "api/account-close" | "api/account-reopen" | "api/account-delete" | "api/account-balance" | "api/categories-get" | "api/category-groups-get" | "api/category-group-create" | "api/category-group-update" | "api/category-group-delete" | "api/category-create" | "api/category-update" | "api/category-delete" | "api/payees-get" | "api/common-payees-get" | "api/payee-create" | "api/payee-update" | "api/payee-delete" | "api/payees-merge" | "api/rules-get" | "api/payee-rules-get" | "api/rule-create" | "api/rule-update" | "api/rule-delete" | "api/schedule-create" | "api/schedule-update" | "api/schedule-delete" | "api/schedules-get" | "api/get-id-by-name" | "api/get-server-version" | "budget/budget-amount" | "budget/copy-previous-month" | "budget/copy-single-month" | "budget/set-zero" | "budget/set-3month-avg" | "budget/set-6month-avg" | "budget/set-12month-avg" | "budget/set-n-month-avg" | "budget/check-templates" | "budget/apply-goal-template" | "budget/apply-multiple-templates" | "budget/overwrite-goal-template" | "budget/apply-single-template" | "budget/cleanup-goal-template" | "budget/hold-for-next-month" | "budget/reset-hold" | "budget/cover-overspending" | "budget/transfer-available" | "budget/cover-overbudgeted" | "budget/transfer-category" | "budget/set-carryover" | "budget/reset-income-carryover" | "get-categories" | "get-budget-bounds" | "envelope-budget-month" | "tracking-budget-month" | "category-create" | "category-update" | "category-move" | "category-delete" | "get-category-groups" | "category-group-create" | "category-group-update" | "category-group-move" | "category-group-delete" | "must-category-transfer" | "budget/get-category-automations" | "budget/set-category-automations" | "budget/store-note-templates" | "budget/render-note-templates" | "dashboard_pages-get" | "dashboard-create" | "dashboard-delete" | "dashboard-rename" | "dashboard-update" | "dashboard-update-widget" | "dashboard-reset" | "dashboard-add-widget" | "dashboard-remove-widget" | "dashboard-copy-widget" | "dashboard-import" | "filter-create" | "filter-update" | "filter-delete" | "notes-save" | "notes-save-undoable" | "preferences/save" | "preferences/get" | "save-global-prefs" | "load-global-prefs" | "save-prefs" | "load-prefs" | "save-server-prefs" | "report/create" | "report/update" | "report/delete" | "rule-validate" | "rule-add" | "rule-update" | "rule-delete" | "rule-delete-all" | "rule-apply-actions" | "rule-add-payee-rename" | "rules-get" | "rule-get" | "rules-run" | "schedule/create" | "schedule/update" | "schedule/delete" | "schedule/skip-next-date" | "schedule/post-transaction" | "schedule/force-run-service" | "schedule/discover" | "schedule/get-upcoming-dates" | "transactions-batch-update" | "transaction-add" | "transaction-update" | "transaction-delete" | "transactions-parse-file" | "transactions-export" | "transactions-export-query" | "transactions-merge" | "get-earliest-transaction" | "get-latest-transaction" | "users-get" | "user-delete-all" | "user-add" | "user-update" | "access-add" | "access-delete-all" | "access-get-available-users" | "transfer-ownership" | "owner-created" | "tools/fix-split-transactions" | "account-update" | "accounts-get" | "account-balance" | "account-properties" | "gocardless-accounts-link" | "simplefin-accounts-link" | "pluggyai-accounts-link" | "account-create" | "account-close" | "account-reopen" | "account-move" | "secret-set" | "secret-check" | "gocardless-poll-web-token" | "gocardless-poll-web-token-stop" | "gocardless-status" | "simplefin-status" | "pluggyai-status" | "simplefin-accounts" | "pluggyai-accounts" | "gocardless-get-banks" | "gocardless-create-web-token" | "accounts-bank-sync" | "simplefin-batch-sync" | "transactions-import" | "account-unlink" | "payee-create" | "common-payees-get" | "payees-get" | "payees-get-orphaned" | "payees-get-rule-counts" | "payees-merge" | "payees-batch-change" | "payees-check-orphaned" | "payees-get-rules" | "get-cell" | "get-cell-names" | "create-query" | "sync-reset" | "sync-repair" | "validate-budget-name" | "unique-budget-name" | "get-budgets" | "get-remote-files" | "reset-budget-cache" | "upload-budget" | "download-budget" | "sync-budget" | "create-demo-budget" | "close-budget" | "delete-budget" | "duplicate-budget" | "create-budget" | "import-budget" | "export-budget" | "upload-file-web" | "backups-get" | "backup-load" | "backup-make" | "get-last-opened-backup" | "key-make" | "key-test" | "tags-get" | "tags-create" | "tags-delete" | "tags-delete-all" | "tags-update" | "tags-find" | "get-did-bootstrap" | "subscribe-needs-bootstrap" | "subscribe-bootstrap" | "subscribe-get-login-methods" | "subscribe-get-user" | "subscribe-change-password" | "subscribe-sign-in" | "subscribe-sign-out" | "subscribe-set-token" | "enable-openid" | "get-openid-config" | "enable-password">(name: Name, func: Handlers[Name]): void;
13
13
  service(func: () => () => void): void;
14
14
  combine(...apps: any[]): void;
15
15
  startServices(): void;
@@ -1,5 +1,10 @@
1
1
  import { type CustomReportEntity } from './reports';
2
2
  import { type RuleConditionEntity } from './rule';
3
+ export type DashboardEntity = {
4
+ id: string;
5
+ name: string;
6
+ tombstone: boolean;
7
+ };
3
8
  export type TimeFrame = {
4
9
  start: string;
5
10
  end: string;
@@ -7,6 +12,7 @@ export type TimeFrame = {
7
12
  };
8
13
  type AbstractWidget<T extends string, Meta extends Record<string, unknown> | null = null> = {
9
14
  id: string;
15
+ dashboard_page_id: string;
10
16
  type: T;
11
17
  x: number;
12
18
  y: number;
@@ -48,7 +54,7 @@ export type CrossoverWidget = AbstractWidget<'crossover-card', {
48
54
  timeFrame?: TimeFrame;
49
55
  safeWithdrawalRate?: number;
50
56
  estimatedReturn?: number | null;
51
- projectionType?: 'trend' | 'hampel';
57
+ projectionType?: 'hampel' | 'median' | 'mean';
52
58
  showHiddenCategories?: boolean;
53
59
  expenseAdjustmentFactor?: number;
54
60
  } | null>;
@@ -58,7 +64,7 @@ export type MarkdownWidget = AbstractWidget<'markdown-card', {
58
64
  }>;
59
65
  type SpecializedWidget = NetWorthWidget | CashFlowWidget | SpendingWidget | CrossoverWidget | MarkdownWidget | SummaryWidget | CalendarWidget | FormulaWidget;
60
66
  export type Widget = SpecializedWidget | CustomReportWidget;
61
- export type NewWidget = Omit<Widget, 'id' | 'tombstone'>;
67
+ export type NewWidget = Omit<Widget, 'id' | 'tombstone' | 'dashboard_page_id'>;
62
68
  export type ExportImportCustomReportWidget = Omit<CustomReportWidget, 'id' | 'meta' | 'tombstone'> & {
63
69
  meta: Omit<CustomReportEntity, 'tombstone'>;
64
70
  };
@@ -0,0 +1 @@
1
+ export default function runMigration(db: any): Promise<void>;
@@ -14365,8 +14365,14 @@ const schema = {
14365
14365
  goal: f("integer"),
14366
14366
  long_goal: f("integer")
14367
14367
  },
14368
+ dashboard_pages: {
14369
+ id: f("id"),
14370
+ name: f("string"),
14371
+ tombstone: f("boolean")
14372
+ },
14368
14373
  dashboard: {
14369
14374
  id: f("id"),
14375
+ dashboard_page_id: f("id", { ref: "dashboard_pages" }),
14370
14376
  type: f("string", { required: true }),
14371
14377
  width: f("integer", { required: true }),
14372
14378
  height: f("integer", { required: true }),
@@ -59696,33 +59702,6 @@ async function listRemoteFiles() {
59696
59702
  hasKey: hasKey(file.encryptKeyId)
59697
59703
  })).filter(Boolean);
59698
59704
  }
59699
- async function getRemoteFile(fileId) {
59700
- const userToken = await getItem("user-token");
59701
- if (!userToken) {
59702
- return null;
59703
- }
59704
- let res;
59705
- try {
59706
- res = await fetchJSON(getServer().SYNC_SERVER + "/get-user-file-info", {
59707
- headers: {
59708
- "X-ACTUAL-TOKEN": userToken,
59709
- "X-ACTUAL-FILE-ID": fileId
59710
- }
59711
- });
59712
- }
59713
- catch (e) {
59714
- logger.log("Unexpected error fetching file from server", e);
59715
- return null;
59716
- }
59717
- if (res.status === "error") {
59718
- logger.log("Error fetching file from server", res);
59719
- return null;
59720
- }
59721
- return {
59722
- ...res.data,
59723
- hasKey: hasKey(res.data.encryptKeyId)
59724
- };
59725
- }
59726
59705
  async function download(cloudFileId) {
59727
59706
  const userToken = await getItem("user-token");
59728
59707
  const syncServer = getServer().SYNC_SERVER;
@@ -102004,7 +101983,7 @@ function getHasTransactionsQuery(schedules) {
102004
101983
  function recurConfigToRSchedule(config2) {
102005
101984
  const base = {
102006
101985
  start: parseDate$1(config2.start),
102007
- // @ts-ignore: issues with https://gitlab.com/john.carroll.p/rschedule/-/issues/86
101986
+ // @ts-expect-error: issues with https://gitlab.com/john.carroll.p/rschedule/-/issues/86
102008
101987
  frequency: config2.frequency.toUpperCase(),
102009
101988
  byHourOfDay: [12]
102010
101989
  };
@@ -121495,7 +121474,7 @@ function requireMd5() {
121495
121474
  }
121496
121475
  var md5Exports = requireMd5();
121497
121476
  const md5 = /* @__PURE__ */ getDefaultExportFromCjs(md5Exports);
121498
- async function runMigration$3(db2) {
121477
+ async function runMigration$4(db2) {
121499
121478
  function getValue(node2) {
121500
121479
  return node2.expr != null ? node2.expr : node2.cachedValue;
121501
121480
  }
@@ -121592,7 +121571,7 @@ CREATE TABLE kvcache_key (id INTEGER PRIMARY KEY, key REAL);
121592
121571
  VACUUM;
121593
121572
  `);
121594
121573
  }
121595
- async function runMigration$2(db2) {
121574
+ async function runMigration$3(db2) {
121596
121575
  const categories = await db2.runQuery("SELECT id FROM categories WHERE tombstone = 0", [], true);
121597
121576
  const customReports = await db2.runQuery("SELECT id, selected_categories, conditions FROM custom_reports WHERE tombstone = 0 AND selected_categories IS NOT NULL", [], true);
121598
121577
  for (const report of customReports) {
@@ -121878,7 +121857,7 @@ const DEFAULT_DASHBOARD_STATE = [
121878
121857
  }
121879
121858
  }
121880
121859
  ];
121881
- async function runMigration$1(db2) {
121860
+ async function runMigration$2(db2) {
121882
121861
  db2.transaction(() => {
121883
121862
  const reports = db2.runQuery("SELECT id FROM custom_reports WHERE tombstone = 0 ORDER BY name COLLATE NOCASE ASC", [], true);
121884
121863
  db2.execQuery(`
@@ -121943,7 +121922,7 @@ const SYNCED_PREF_KEYS = [
121943
121922
  "budgetType",
121944
121923
  /^flags\./
121945
121924
  ];
121946
- async function runMigration(db2, { fs: fs2, fileId }) {
121925
+ async function runMigration$1(db2, { fs: fs2, fileId }) {
121947
121926
  await db2.execQuery(`
121948
121927
  CREATE TABLE preferences
121949
121928
  (id TEXT PRIMARY KEY,
@@ -121969,12 +121948,34 @@ async function runMigration(db2, { fs: fs2, fileId }) {
121969
121948
  catch {
121970
121949
  }
121971
121950
  }
121951
+ async function runMigration(db2) {
121952
+ db2.transaction(() => {
121953
+ db2.execQuery(`
121954
+ CREATE TABLE dashboard_pages
121955
+ (id TEXT PRIMARY KEY,
121956
+ name TEXT,
121957
+ tombstone INTEGER DEFAULT 0);
121958
+ `);
121959
+ db2.execQuery(`
121960
+ ALTER TABLE dashboard ADD COLUMN dashboard_page_id TEXT;
121961
+ `);
121962
+ const defaultDashboardId = v4();
121963
+ db2.runQuery(`INSERT INTO dashboard_pages (id, name) VALUES (?, ?)`, [
121964
+ defaultDashboardId,
121965
+ "Main"
121966
+ ]);
121967
+ db2.runQuery(`UPDATE dashboard SET dashboard_page_id = ?`, [
121968
+ defaultDashboardId
121969
+ ]);
121970
+ });
121971
+ }
121972
121972
  let MIGRATIONS_DIR = migrationsPath;
121973
121973
  const javascriptMigrations = {
121974
- 1632571489012: runMigration$3,
121975
- 1722717601e3: runMigration$2,
121976
- 1722804019e3: runMigration$1,
121977
- 1723665565e3: runMigration
121974
+ 1632571489012: runMigration$4,
121975
+ 1722717601e3: runMigration$3,
121976
+ 1722804019e3: runMigration$2,
121977
+ 1723665565e3: runMigration$1,
121978
+ 1765518577215: runMigration
121978
121979
  };
121979
121980
  function getMigrationId(name) {
121980
121981
  return parseInt(name.match(/^(\d)+/)[0]);
@@ -122277,7 +122278,6 @@ app$d.method("validate-budget-name", handleValidateBudgetName);
122277
122278
  app$d.method("unique-budget-name", handleUniqueBudgetName);
122278
122279
  app$d.method("get-budgets", getBudgets);
122279
122280
  app$d.method("get-remote-files", getRemoteFiles);
122280
- app$d.method("get-user-file-info", getUserFileInfo);
122281
122281
  app$d.method("reset-budget-cache", mutator(resetBudgetCache));
122282
122282
  app$d.method("upload-budget", uploadBudget);
122283
122283
  app$d.method("download-budget", downloadBudget);
@@ -122332,9 +122332,6 @@ async function getBudgets() {
122332
122332
  async function getRemoteFiles() {
122333
122333
  return listRemoteFiles();
122334
122334
  }
122335
- async function getUserFileInfo(fileId) {
122336
- return getRemoteFile(fileId);
122337
- }
122338
122335
  async function resetBudgetCache() {
122339
122336
  await loadUserBudgets(db$1);
122340
122337
  get$3().recomputeAll();
@@ -124461,6 +124458,19 @@ app$c.method("report/delete", mutator(undoable(deleteReport)));
124461
124458
  function isExportedCustomReportWidget(widget) {
124462
124459
  return widget.type === "custom-report";
124463
124460
  }
124461
+ function isWidgetType(type2) {
124462
+ return [
124463
+ "net-worth-card",
124464
+ "cash-flow-card",
124465
+ "spending-card",
124466
+ "crossover-card",
124467
+ "markdown-card",
124468
+ "summary-card",
124469
+ "calendar-card",
124470
+ "formula-card",
124471
+ "custom-report"
124472
+ ].includes(type2);
124473
+ }
124464
124474
  const exportModel = {
124465
124475
  validate(dashboard) {
124466
124476
  requiredFields("Dashboard", dashboard, ["version", "widgets"]);
@@ -124488,17 +124498,7 @@ const exportModel = {
124488
124498
  if (!Number.isInteger(widget.height)) {
124489
124499
  throw new ValidationError(`Invalid widget.${idx}.height data-type for value ${widget.height}.`);
124490
124500
  }
124491
- if (![
124492
- "net-worth-card",
124493
- "cash-flow-card",
124494
- "spending-card",
124495
- "crossover-card",
124496
- "custom-report",
124497
- "markdown-card",
124498
- "summary-card",
124499
- "calendar-card",
124500
- "formula-card"
124501
- ].includes(widget.type)) {
124501
+ if (!isWidgetType(widget.type)) {
124502
124502
  throw new ValidationError(`Invalid widget.${idx}.type value ${widget.type}.`);
124503
124503
  }
124504
124504
  if (isExportedCustomReportWidget(widget)) {
@@ -124507,6 +124507,28 @@ const exportModel = {
124507
124507
  });
124508
124508
  }
124509
124509
  };
124510
+ async function getDashboardPages() {
124511
+ return all("SELECT * FROM dashboard_pages WHERE tombstone = 0");
124512
+ }
124513
+ async function createDashboardPage({ name }) {
124514
+ const id2 = v4();
124515
+ await insertWithSchema("dashboard_pages", { id: id2, name });
124516
+ return id2;
124517
+ }
124518
+ async function deleteDashboardPage(id2) {
124519
+ const res = await first$2("SELECT count(*) as c FROM dashboard_pages WHERE tombstone = 0");
124520
+ if ((res?.c ?? 0) <= 1) {
124521
+ throw new Error("Cannot delete the last dashboard page");
124522
+ }
124523
+ const deleting_widgets = await all("SELECT id FROM dashboard WHERE dashboard_page_id = ? AND tombstone = 0", [id2]);
124524
+ await batchMessages(async () => {
124525
+ await delete_("dashboard_pages", id2);
124526
+ await Promise.all(deleting_widgets.map(({ id: id22 }) => delete_("dashboard", id22)));
124527
+ });
124528
+ }
124529
+ async function renameDashboardPage({ id: id2, name }) {
124530
+ await updateWithSchema("dashboard_pages", { id: id2, name });
124531
+ }
124510
124532
  async function updateDashboard(widgets) {
124511
124533
  const { data: dbWidgets } = await aqlQuery(q("dashboard").filter({ id: { $oneof: widgets.map(({ id: id2 }) => id2) } }).select("*"));
124512
124534
  const dbWidgetMap = new Map(dbWidgets.map((widget) => [widget.id, widget]));
@@ -124515,19 +124537,20 @@ async function updateDashboard(widgets) {
124515
124537
  async function updateDashboardWidget(widget) {
124516
124538
  await updateWithSchema("dashboard", widget);
124517
124539
  }
124518
- async function resetDashboard() {
124540
+ async function resetDashboard(id2) {
124519
124541
  await batchMessages(async () => {
124542
+ const widgets = await selectWithSchema("dashboard", "SELECT id FROM dashboard WHERE dashboard_page_id = ? AND tombstone = 0", [id2]);
124520
124543
  await Promise.all([
124521
- // Delete all widgets
124522
- deleteAll("dashboard"),
124544
+ // Delete all widgets for this dashboard
124545
+ ...widgets.map(({ id: id22 }) => delete_("dashboard", id22)),
124523
124546
  // Insert the default state
124524
- ...DEFAULT_DASHBOARD_STATE.map((widget) => insertWithSchema("dashboard", widget))
124547
+ ...DEFAULT_DASHBOARD_STATE.map((widget) => insertWithSchema("dashboard", { ...widget, dashboard_page_id: id2 }))
124525
124548
  ]);
124526
124549
  });
124527
124550
  }
124528
124551
  async function addDashboardWidget(widget) {
124529
124552
  if (!("x" in widget) && !("y" in widget)) {
124530
- const data = await first$2("SELECT x, y, width, height FROM dashboard WHERE tombstone = 0 ORDER BY y DESC, x DESC");
124553
+ const data = await first$2("SELECT x, y, width, height FROM dashboard WHERE dashboard_page_id = ? AND tombstone = 0 ORDER BY y DESC, x DESC", [widget.dashboard_page_id]);
124531
124554
  if (!data) {
124532
124555
  widget.x = 0;
124533
124556
  widget.y = 0;
@@ -124538,12 +124561,37 @@ async function addDashboardWidget(widget) {
124538
124561
  widget.y = data.y + (xBoundaryCheck > 12 ? data.height : 0);
124539
124562
  }
124540
124563
  }
124541
- await insertWithSchema("dashboard", widget);
124564
+ const { dashboard_page_id, ...widgetWithoutDashboardPageId } = widget;
124565
+ await insertWithSchema("dashboard", {
124566
+ ...widgetWithoutDashboardPageId,
124567
+ dashboard_page_id
124568
+ });
124542
124569
  }
124543
124570
  async function removeDashboardWidget(widgetId) {
124544
124571
  await delete_("dashboard", widgetId);
124545
124572
  }
124546
- async function importDashboard({ filepath }) {
124573
+ async function copyDashboardWidget({ widgetId, targetDashboardPageId }) {
124574
+ const widget = await first$2("SELECT * FROM dashboard WHERE id = ? AND tombstone = 0", [widgetId]);
124575
+ if (!widget) {
124576
+ throw new Error(`Widget not found: ${widgetId}`);
124577
+ }
124578
+ await batchMessages(async () => {
124579
+ if (isWidgetType(widget.type)) {
124580
+ const newWidget = {
124581
+ type: widget.type,
124582
+ width: widget.width,
124583
+ height: widget.height,
124584
+ meta: widget.meta ? JSON.parse(widget.meta) : {},
124585
+ dashboard_page_id: targetDashboardPageId
124586
+ };
124587
+ await addDashboardWidget(newWidget);
124588
+ }
124589
+ else {
124590
+ throw new Error(`Unsupported widget type: ${widget.type}`);
124591
+ }
124592
+ });
124593
+ }
124594
+ async function importDashboard({ filepath, dashboardPageId }) {
124547
124595
  try {
124548
124596
  if (!await exists(filepath)) {
124549
124597
  throw new Error(`File not found at the provided path: ${filepath}`);
@@ -124553,10 +124601,11 @@ async function importDashboard({ filepath }) {
124553
124601
  exportModel.validate(parsedContent);
124554
124602
  const customReportIds = await all("SELECT id from custom_reports");
124555
124603
  const customReportIdSet = new Set(customReportIds.map(({ id: id2 }) => id2));
124604
+ const existingWidgets = await selectWithSchema("dashboard", "SELECT id FROM dashboard WHERE dashboard_page_id = ? AND tombstone = 0", [dashboardPageId]);
124556
124605
  await batchMessages(async () => {
124557
124606
  await Promise.all([
124558
124607
  // Delete all widgets
124559
- deleteAll("dashboard"),
124608
+ ...existingWidgets.map(({ id: id2 }) => delete_("dashboard", id2)),
124560
124609
  // Insert new widgets
124561
124610
  ...parsedContent.widgets.map((widget) => insertWithSchema("dashboard", {
124562
124611
  type: widget.type,
@@ -124564,6 +124613,7 @@ async function importDashboard({ filepath }) {
124564
124613
  height: widget.height,
124565
124614
  x: widget.x,
124566
124615
  y: widget.y,
124616
+ dashboard_page_id: dashboardPageId,
124567
124617
  meta: isExportedCustomReportWidget(widget) ? { id: widget.meta.id } : widget.meta
124568
124618
  })),
124569
124619
  // Insert new custom reports
@@ -124597,11 +124647,16 @@ async function importDashboard({ filepath }) {
124597
124647
  }
124598
124648
  }
124599
124649
  const app$b = createApp();
124650
+ app$b.method("dashboard_pages-get", getDashboardPages);
124651
+ app$b.method("dashboard-create", mutator(undoable(createDashboardPage)));
124652
+ app$b.method("dashboard-delete", mutator(undoable(deleteDashboardPage)));
124653
+ app$b.method("dashboard-rename", mutator(undoable(renameDashboardPage)));
124600
124654
  app$b.method("dashboard-update", mutator(undoable(updateDashboard)));
124601
124655
  app$b.method("dashboard-update-widget", mutator(undoable(updateDashboardWidget)));
124602
124656
  app$b.method("dashboard-reset", mutator(undoable(resetDashboard)));
124603
124657
  app$b.method("dashboard-add-widget", mutator(undoable(addDashboardWidget)));
124604
124658
  app$b.method("dashboard-remove-widget", mutator(undoable(removeDashboardWidget)));
124659
+ app$b.method("dashboard-copy-widget", mutator(undoable(copyDashboardWidget)));
124605
124660
  app$b.method("dashboard-import", mutator(undoable(importDashboard)));
124606
124661
  const app$a = createApp();
124607
124662
  app$a.method("key-make", keyMake);
package/dist/index.js CHANGED
@@ -39,6 +39,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
39
39
  exports.utils = exports.internal = void 0;
40
40
  exports.init = init;
41
41
  exports.shutdown = shutdown;
42
+ // oxlint-disable-next-line typescript/ban-ts-comment
42
43
  // @ts-ignore: bundle not available until we build it
43
44
  const bundle = __importStar(require("./app/bundle.api.js"));
44
45
  const injected = __importStar(require("./injected"));
@@ -0,0 +1,29 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.default = runMigration;
4
+ const uuid_1 = require("uuid");
5
+ async function runMigration(db) {
6
+ db.transaction(() => {
7
+ // 1. Create dashboards table
8
+ db.execQuery(`
9
+ CREATE TABLE dashboard_pages
10
+ (id TEXT PRIMARY KEY,
11
+ name TEXT,
12
+ tombstone INTEGER DEFAULT 0);
13
+ `);
14
+ // 2. Add dashboard_page_id to dashboard (widgets) table
15
+ db.execQuery(`
16
+ ALTER TABLE dashboard ADD COLUMN dashboard_page_id TEXT;
17
+ `);
18
+ // 3. Create a default dashboard
19
+ const defaultDashboardId = (0, uuid_1.v4)();
20
+ db.runQuery(`INSERT INTO dashboard_pages (id, name) VALUES (?, ?)`, [
21
+ defaultDashboardId,
22
+ 'Main',
23
+ ]);
24
+ // 4. Migrate existing widgets to the default dashboard
25
+ db.runQuery(`UPDATE dashboard SET dashboard_page_id = ?`, [
26
+ defaultDashboardId,
27
+ ]);
28
+ });
29
+ }
package/dist/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@actual-app/api",
3
- "version": "26.2.0-nightly.20260113",
3
+ "version": "26.2.0-nightly.20260114",
4
4
  "description": "An API for Actual",
5
5
  "license": "MIT",
6
6
  "files": [
package/dist/utils.js CHANGED
@@ -34,6 +34,7 @@ var __importStar = (this && this.__importStar) || (function () {
34
34
  })();
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
36
  exports.integerToAmount = exports.amountToInteger = void 0;
37
+ // oxlint-disable-next-line typescript/ban-ts-comment
37
38
  // @ts-ignore: bundle not available until we build it
38
39
  const bundle = __importStar(require("./app/bundle.api.js"));
39
40
  exports.amountToInteger = bundle.lib.amountToInteger;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@actual-app/api",
3
- "version": "26.2.0-nightly.20260113",
3
+ "version": "26.2.0-nightly.20260114",
4
4
  "description": "An API for Actual",
5
5
  "license": "MIT",
6
6
  "files": [