cata-centavo 0.1.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/LICENSE +21 -0
- package/README.md +159 -0
- package/dist/bin/cata-centavo.js +226 -0
- package/dist/bin/cata-centavo.js.map +1 -0
- package/dist/cli/dispatch.js +67 -0
- package/dist/cli/dispatch.js.map +1 -0
- package/dist/cli/doctor.js +178 -0
- package/dist/cli/doctor.js.map +1 -0
- package/dist/cli/init.js +125 -0
- package/dist/cli/init.js.map +1 -0
- package/dist/cli/progress.js +85 -0
- package/dist/cli/progress.js.map +1 -0
- package/dist/cli/sync.js +26 -0
- package/dist/cli/sync.js.map +1 -0
- package/dist/config.js +119 -0
- package/dist/config.js.map +1 -0
- package/dist/core/account.js +12 -0
- package/dist/core/account.js.map +1 -0
- package/dist/core/accounts.js +91 -0
- package/dist/core/accounts.js.map +1 -0
- package/dist/core/aggregate.js +83 -0
- package/dist/core/aggregate.js.map +1 -0
- package/dist/core/balance.js +68 -0
- package/dist/core/balance.js.map +1 -0
- package/dist/core/bill-rows.js +132 -0
- package/dist/core/bill-rows.js.map +1 -0
- package/dist/core/bill.js +103 -0
- package/dist/core/bill.js.map +1 -0
- package/dist/core/category-source.js +26 -0
- package/dist/core/category-source.js.map +1 -0
- package/dist/core/category.js +72 -0
- package/dist/core/category.js.map +1 -0
- package/dist/core/consent.js +19 -0
- package/dist/core/consent.js.map +1 -0
- package/dist/core/contracts.js +9 -0
- package/dist/core/contracts.js.map +1 -0
- package/dist/core/counterparty.js +69 -0
- package/dist/core/counterparty.js.map +1 -0
- package/dist/core/date.js +25 -0
- package/dist/core/date.js.map +1 -0
- package/dist/core/description.js +58 -0
- package/dist/core/description.js.map +1 -0
- package/dist/core/diagnose.js +32 -0
- package/dist/core/diagnose.js.map +1 -0
- package/dist/core/mcc.js +124 -0
- package/dist/core/mcc.js.map +1 -0
- package/dist/core/refresh.js +142 -0
- package/dist/core/refresh.js.map +1 -0
- package/dist/core/self-transfer.js +22 -0
- package/dist/core/self-transfer.js.map +1 -0
- package/dist/core/taxonomy-tree.js +148 -0
- package/dist/core/taxonomy-tree.js.map +1 -0
- package/dist/core/taxonomy.js +70 -0
- package/dist/core/taxonomy.js.map +1 -0
- package/dist/core/transaction.js +2 -0
- package/dist/core/transaction.js.map +1 -0
- package/dist/core/transactions.js +46 -0
- package/dist/core/transactions.js.map +1 -0
- package/dist/logging.js +93 -0
- package/dist/logging.js.map +1 -0
- package/dist/mcp/cursor.js +64 -0
- package/dist/mcp/cursor.js.map +1 -0
- package/dist/mcp/format.js +38 -0
- package/dist/mcp/format.js.map +1 -0
- package/dist/mcp/server.js +50 -0
- package/dist/mcp/server.js.map +1 -0
- package/dist/mcp/source.js +2 -0
- package/dist/mcp/source.js.map +1 -0
- package/dist/mcp/tools/accounts.js +144 -0
- package/dist/mcp/tools/accounts.js.map +1 -0
- package/dist/mcp/tools/balance.js +96 -0
- package/dist/mcp/tools/balance.js.map +1 -0
- package/dist/mcp/tools/bill-summary-format.js +161 -0
- package/dist/mcp/tools/bill-summary-format.js.map +1 -0
- package/dist/mcp/tools/bill-summary.js +105 -0
- package/dist/mcp/tools/bill-summary.js.map +1 -0
- package/dist/mcp/tools/bills.js +144 -0
- package/dist/mcp/tools/bills.js.map +1 -0
- package/dist/mcp/tools/closing-days.js +90 -0
- package/dist/mcp/tools/closing-days.js.map +1 -0
- package/dist/mcp/tools/list-transactions.js +122 -0
- package/dist/mcp/tools/list-transactions.js.map +1 -0
- package/dist/mcp/tools/result.js +12 -0
- package/dist/mcp/tools/result.js.map +1 -0
- package/dist/mcp/tools/set-category.js +74 -0
- package/dist/mcp/tools/set-category.js.map +1 -0
- package/dist/mcp/tools/sources.js +73 -0
- package/dist/mcp/tools/sources.js.map +1 -0
- package/dist/mcp/tools/transaction-details.js +146 -0
- package/dist/mcp/tools/transaction-details.js.map +1 -0
- package/dist/mcp/tools/transaction-input.js +51 -0
- package/dist/mcp/tools/transaction-input.js.map +1 -0
- package/dist/mcp/tools/transactions.js +186 -0
- package/dist/mcp/tools/transactions.js.map +1 -0
- package/dist/pluggy/client.js +123 -0
- package/dist/pluggy/client.js.map +1 -0
- package/dist/pluggy/errors.js +152 -0
- package/dist/pluggy/errors.js.map +1 -0
- package/dist/pluggy/mapper.js +155 -0
- package/dist/pluggy/mapper.js.map +1 -0
- package/dist/pluggy/money.js +53 -0
- package/dist/pluggy/money.js.map +1 -0
- package/dist/pluggy/transaction-mapper.js +124 -0
- package/dist/pluggy/transaction-mapper.js.map +1 -0
- package/dist/pluggy/transport.js +170 -0
- package/dist/pluggy/transport.js.map +1 -0
- package/dist/pluggy/wire.js +207 -0
- package/dist/pluggy/wire.js.map +1 -0
- package/dist/storage/categories.js +101 -0
- package/dist/storage/categories.js.map +1 -0
- package/dist/storage/category-sql.js +37 -0
- package/dist/storage/category-sql.js.map +1 -0
- package/dist/storage/closing-days.js +27 -0
- package/dist/storage/closing-days.js.map +1 -0
- package/dist/storage/db.js +162 -0
- package/dist/storage/db.js.map +1 -0
- package/dist/storage/diagnostics.js +57 -0
- package/dist/storage/diagnostics.js.map +1 -0
- package/dist/storage/harvest.js +87 -0
- package/dist/storage/harvest.js.map +1 -0
- package/dist/storage/migrations.js +125 -0
- package/dist/storage/migrations.js.map +1 -0
- package/dist/storage/transaction-row.js +74 -0
- package/dist/storage/transaction-row.js.map +1 -0
- package/dist/storage/transactions.js +213 -0
- package/dist/storage/transactions.js.map +1 -0
- package/package.json +70 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"doctor.js","sourceRoot":"","sources":["../../src/cli/doctor.ts"],"names":[],"mappings":"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"}
|
package/dist/cli/init.js
ADDED
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import { loadConfig } from "../config.js";
|
|
2
|
+
import { describeSync } from "./sync.js";
|
|
3
|
+
export async function runInit(deps) {
|
|
4
|
+
const config = loadConfig(deps.env);
|
|
5
|
+
if (!config.ok) {
|
|
6
|
+
return { kind: "config", problems: config.problems };
|
|
7
|
+
}
|
|
8
|
+
// Before the network, because a data.db this release cannot read is worth
|
|
9
|
+
// saying out loud rather than after a round of API calls.
|
|
10
|
+
let storage;
|
|
11
|
+
try {
|
|
12
|
+
storage = deps.prepareStorage();
|
|
13
|
+
}
|
|
14
|
+
catch (error) {
|
|
15
|
+
return { kind: "storage", reason: describe(error) };
|
|
16
|
+
}
|
|
17
|
+
const bank = deps.createBank(config.config.credentials);
|
|
18
|
+
try {
|
|
19
|
+
await bank.verifyCredentials();
|
|
20
|
+
}
|
|
21
|
+
catch (error) {
|
|
22
|
+
return { kind: "credentials", storage, reason: describe(error) };
|
|
23
|
+
}
|
|
24
|
+
const outcomes = await Promise.all(config.config.itemIds.map((id) => check(bank, id)));
|
|
25
|
+
return { kind: "checked", storage, outcomes };
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* One bad id does not abort the rest. A report covering every connection is worth
|
|
29
|
+
* more than one that stops at the first bad line, and the exit code carries the
|
|
30
|
+
* failure instead.
|
|
31
|
+
*/
|
|
32
|
+
async function check(bank, id) {
|
|
33
|
+
try {
|
|
34
|
+
return { kind: "usable", id, connection: await bank.getConnection(id) };
|
|
35
|
+
}
|
|
36
|
+
catch (error) {
|
|
37
|
+
return { kind: "failed", id, reason: describe(error) };
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
export function exitCodeFor(report) {
|
|
41
|
+
switch (report.kind) {
|
|
42
|
+
case "config":
|
|
43
|
+
return 2;
|
|
44
|
+
case "storage":
|
|
45
|
+
case "credentials":
|
|
46
|
+
return 1;
|
|
47
|
+
case "checked":
|
|
48
|
+
if (report.outcomes.every((outcome) => outcome.kind === "usable")) {
|
|
49
|
+
return 0;
|
|
50
|
+
}
|
|
51
|
+
return 1;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
/** Lines for stderr. Nothing here may reach stdout (ADR §4). */
|
|
55
|
+
export function formatInit(report, clock) {
|
|
56
|
+
const configRead = "✓ configuration read from the environment";
|
|
57
|
+
switch (report.kind) {
|
|
58
|
+
case "config":
|
|
59
|
+
return ["✗ configuration is incomplete", ...report.problems.map((problem) => ` ${problem}`)];
|
|
60
|
+
case "storage":
|
|
61
|
+
return [configRead, `✗ local storage is unusable: ${report.reason}`];
|
|
62
|
+
case "credentials":
|
|
63
|
+
return [configRead, ...formatStorage(report.storage), `✗ ${report.reason}`];
|
|
64
|
+
case "checked": {
|
|
65
|
+
const usable = report.outcomes.filter((outcome) => outcome.kind === "usable").length;
|
|
66
|
+
return [
|
|
67
|
+
configRead,
|
|
68
|
+
...formatStorage(report.storage),
|
|
69
|
+
"✓ credentials accepted",
|
|
70
|
+
...report.outcomes.flatMap((outcome) => formatOutcome(outcome, clock)),
|
|
71
|
+
"",
|
|
72
|
+
`${usable} of ${report.outcomes.length} connections are usable`,
|
|
73
|
+
];
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
function formatStorage(storage) {
|
|
78
|
+
return [
|
|
79
|
+
`✓ cache v${storage.cacheVersion} ${storage.cacheDb}`,
|
|
80
|
+
`✓ data v${storage.dataVersion} ${storage.dataDb}`,
|
|
81
|
+
];
|
|
82
|
+
}
|
|
83
|
+
function formatOutcome(outcome, clock) {
|
|
84
|
+
if (outcome.kind === "failed") {
|
|
85
|
+
return [`✗ ${outcome.id} — ${outcome.reason}`];
|
|
86
|
+
}
|
|
87
|
+
return formatUsable(outcome, clock);
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* A `!` rather than a `✓`, because a product that hit its Open Finance monthly
|
|
91
|
+
* quota went unsynced and the numbers below it are older than the rest. Worth
|
|
92
|
+
* saying out loud, and not worth failing over.
|
|
93
|
+
*/
|
|
94
|
+
function formatUsable(outcome, clock) {
|
|
95
|
+
const { institution, status, lastUpdatedAt, parameter, warnings } = outcome.connection;
|
|
96
|
+
const state = `${institution}, ${status}, ${describeSync(lastUpdatedAt, clock.now())}`;
|
|
97
|
+
let marker;
|
|
98
|
+
if (warnings.length > 0) {
|
|
99
|
+
marker = "!";
|
|
100
|
+
}
|
|
101
|
+
else {
|
|
102
|
+
marker = "✓";
|
|
103
|
+
}
|
|
104
|
+
return [
|
|
105
|
+
`${marker} ${outcome.id} — ${state}${waitingOn(parameter)}`,
|
|
106
|
+
...warnings.map((warning) => ` ${warning}`),
|
|
107
|
+
];
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* A read-only fact rather than a verdict: the connection is as usable as any
|
|
111
|
+
* other, and the bank happens to have named what it is waiting on.
|
|
112
|
+
*/
|
|
113
|
+
function waitingOn(parameter) {
|
|
114
|
+
if (parameter === null) {
|
|
115
|
+
return "";
|
|
116
|
+
}
|
|
117
|
+
return ` — the bank is waiting on you: ${parameter}`;
|
|
118
|
+
}
|
|
119
|
+
function describe(error) {
|
|
120
|
+
if (error instanceof Error) {
|
|
121
|
+
return error.message;
|
|
122
|
+
}
|
|
123
|
+
return String(error);
|
|
124
|
+
}
|
|
125
|
+
//# sourceMappingURL=init.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"init.js","sourceRoot":"","sources":["../../src/cli/init.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAA8B,MAAM,cAAc,CAAC;AAEtE,OAAO,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AAsCzC,MAAM,CAAC,KAAK,UAAU,OAAO,CAAC,IAAc;IAC1C,MAAM,MAAM,GAAG,UAAU,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACpC,IAAI,CAAC,MAAM,CAAC,EAAE,EAAE,CAAC;QACf,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,CAAC,QAAQ,EAAE,CAAC;IACvD,CAAC;IAED,0EAA0E;IAC1E,0DAA0D;IAC1D,IAAI,OAAoB,CAAC;IACzB,IAAI,CAAC;QACH,OAAO,GAAG,IAAI,CAAC,cAAc,EAAE,CAAC;IAClC,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,EAAE,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;IACtD,CAAC;IAED,MAAM,IAAI,GAAG,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC;IAExD,IAAI,CAAC;QACH,MAAM,IAAI,CAAC,iBAAiB,EAAE,CAAC;IACjC,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,EAAE,IAAI,EAAE,aAAa,EAAE,OAAO,EAAE,MAAM,EAAE,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;IACnE,CAAC;IAED,MAAM,QAAQ,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC;IAEvF,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,QAAQ,EAAE,CAAC;AAChD,CAAC;AAED;;;;GAIG;AACH,KAAK,UAAU,KAAK,CAAC,IAAU,EAAE,EAAU;IACzC,IAAI,CAAC;QACH,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE,EAAE,UAAU,EAAE,MAAM,IAAI,CAAC,aAAa,CAAC,EAAE,CAAC,EAAE,CAAC;IAC1E,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;IACzD,CAAC;AACH,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,MAAkB;IAC5C,QAAQ,MAAM,CAAC,IAAI,EAAE,CAAC;QACpB,KAAK,QAAQ;YACX,OAAO,CAAC,CAAC;QACX,KAAK,SAAS,CAAC;QACf,KAAK,aAAa;YAChB,OAAO,CAAC,CAAC;QACX,KAAK,SAAS;YACZ,IAAI,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,IAAI,KAAK,QAAQ,CAAC,EAAE,CAAC;gBAClE,OAAO,CAAC,CAAC;YACX,CAAC;YACD,OAAO,CAAC,CAAC;IACb,CAAC;AACH,CAAC;AAED,gEAAgE;AAChE,MAAM,UAAU,UAAU,CAAC,MAAkB,EAAE,KAAY;IACzD,MAAM,UAAU,GAAG,2CAA2C,CAAC;IAE/D,QAAQ,MAAM,CAAC,IAAI,EAAE,CAAC;QACpB,KAAK,QAAQ;YACX,OAAO,CAAC,+BAA+B,EAAE,GAAG,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,KAAK,OAAO,EAAE,CAAC,CAAC,CAAC;QAEhG,KAAK,SAAS;YACZ,OAAO,CAAC,UAAU,EAAE,gCAAgC,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC;QAEvE,KAAK,aAAa;YAChB,OAAO,CAAC,UAAU,EAAE,GAAG,aAAa,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE,KAAK,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC;QAE9E,KAAK,SAAS,CAAC,CAAC,CAAC;YACf,MAAM,MAAM,GAAG,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC,MAAM,CAAC;YAErF,OAAO;gBACL,UAAU;gBACV,GAAG,aAAa,CAAC,MAAM,CAAC,OAAO,CAAC;gBAChC,wBAAwB;gBACxB,GAAG,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,aAAa,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;gBACtE,EAAE;gBACF,GAAG,MAAM,OAAO,MAAM,CAAC,QAAQ,CAAC,MAAM,yBAAyB;aAChE,CAAC;QACJ,CAAC;IACH,CAAC;AACH,CAAC;AAED,SAAS,aAAa,CAAC,OAAoB;IACzC,OAAO;QACL,cAAc,OAAO,CAAC,YAAY,KAAK,OAAO,CAAC,OAAO,EAAE;QACxD,cAAc,OAAO,CAAC,WAAW,KAAK,OAAO,CAAC,MAAM,EAAE;KACvD,CAAC;AACJ,CAAC;AAED,SAAS,aAAa,CAAC,OAA0B,EAAE,KAAY;IAC7D,IAAI,OAAO,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;QAC9B,OAAO,CAAC,KAAK,OAAO,CAAC,EAAE,MAAM,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;IACjD,CAAC;IACD,OAAO,YAAY,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;AACtC,CAAC;AAED;;;;GAIG;AACH,SAAS,YAAY,CACnB,OAAgE,EAChE,KAAY;IAEZ,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,aAAa,EAAE,SAAS,EAAE,QAAQ,EAAE,GAAG,OAAO,CAAC,UAAU,CAAC;IACvF,MAAM,KAAK,GAAG,GAAG,WAAW,KAAK,MAAM,KAAK,YAAY,CAAC,aAAa,EAAE,KAAK,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC;IAEvF,IAAI,MAAc,CAAC;IACnB,IAAI,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACxB,MAAM,GAAG,GAAG,CAAC;IACf,CAAC;SAAM,CAAC;QACN,MAAM,GAAG,GAAG,CAAC;IACf,CAAC;IAED,OAAO;QACL,GAAG,MAAM,IAAI,OAAO,CAAC,EAAE,MAAM,KAAK,GAAG,SAAS,CAAC,SAAS,CAAC,EAAE;QAC3D,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,OAAO,EAAE,CAAC;KAC/C,CAAC;AACJ,CAAC;AAED;;;GAGG;AACH,SAAS,SAAS,CAAC,SAAwB;IACzC,IAAI,SAAS,KAAK,IAAI,EAAE,CAAC;QACvB,OAAO,EAAE,CAAC;IACZ,CAAC;IACD,OAAO,kCAAkC,SAAS,EAAE,CAAC;AACvD,CAAC;AAED,SAAS,QAAQ,CAAC,KAAc;IAC9B,IAAI,KAAK,YAAY,KAAK,EAAE,CAAC;QAC3B,OAAO,KAAK,CAAC,OAAO,CAAC;IACvB,CAAC;IACD,OAAO,MAAM,CAAC,KAAK,CAAC,CAAC;AACvB,CAAC"}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The live region `init` draws while it waits on the banks.
|
|
3
|
+
*
|
|
4
|
+
* A refresh can take minutes — Pluggy documents the login step alone taking up
|
|
5
|
+
* to five — so silence is not an option. On a terminal this redraws a block of
|
|
6
|
+
* lines in place with a spinner; anywhere else it appends a line whenever a stage
|
|
7
|
+
* changes, because escape codes in a log file are worse than no animation.
|
|
8
|
+
*
|
|
9
|
+
* Everything about time and terminal-ness is injected, which is what makes the
|
|
10
|
+
* whole thing testable without a PTY. `write` is the only sink: nothing here may
|
|
11
|
+
* reach stdout, which in server mode *is* the protocol channel (ADR §4).
|
|
12
|
+
*/
|
|
13
|
+
export const SPINNER = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"];
|
|
14
|
+
export const FRAME_MS = 80;
|
|
15
|
+
const HIDE_CURSOR = "\x1b[?25l";
|
|
16
|
+
const SHOW_CURSOR = "\x1b[?25h";
|
|
17
|
+
const CLEAR_LINE = "\x1b[2K";
|
|
18
|
+
const up = (lines) => (lines > 0 ? `\x1b[${lines}A` : "");
|
|
19
|
+
export function createProgress(deps) {
|
|
20
|
+
return deps.tty ? liveRegion(deps) : appendOnChange(deps);
|
|
21
|
+
}
|
|
22
|
+
function liveRegion(deps) {
|
|
23
|
+
let rows = [];
|
|
24
|
+
let drawn = 0;
|
|
25
|
+
let frame = 0;
|
|
26
|
+
let ticker = null;
|
|
27
|
+
let stopped = false;
|
|
28
|
+
function draw() {
|
|
29
|
+
const lines = rows.map((row) => `${SPINNER[frame % SPINNER.length] ?? ""} ${row.label} — ${row.detail}…`);
|
|
30
|
+
const surplus = Math.max(0, drawn - lines.length);
|
|
31
|
+
const painted = lines.map((line) => `${CLEAR_LINE}${line}\n`).join("");
|
|
32
|
+
const cleared = `${CLEAR_LINE}\n`.repeat(surplus);
|
|
33
|
+
deps.write(`${up(drawn)}${painted}${cleared}${up(surplus)}`);
|
|
34
|
+
drawn = lines.length;
|
|
35
|
+
}
|
|
36
|
+
return {
|
|
37
|
+
update: (next) => {
|
|
38
|
+
if (stopped || (next.length === 0 && drawn === 0)) {
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
rows = next;
|
|
42
|
+
if (ticker === null) {
|
|
43
|
+
deps.write(HIDE_CURSOR);
|
|
44
|
+
ticker = deps.interval(() => {
|
|
45
|
+
frame += 1;
|
|
46
|
+
draw();
|
|
47
|
+
}, FRAME_MS);
|
|
48
|
+
}
|
|
49
|
+
draw();
|
|
50
|
+
},
|
|
51
|
+
stop: () => {
|
|
52
|
+
if (stopped) {
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
stopped = true;
|
|
56
|
+
ticker?.cancel();
|
|
57
|
+
ticker = null;
|
|
58
|
+
if (drawn > 0) {
|
|
59
|
+
rows = [];
|
|
60
|
+
draw();
|
|
61
|
+
}
|
|
62
|
+
deps.write(SHOW_CURSOR);
|
|
63
|
+
},
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* A stage per line, once. Repeating "logging in" forty times as the poll loop
|
|
68
|
+
* comes back around would bury the report that follows it.
|
|
69
|
+
*/
|
|
70
|
+
function appendOnChange(deps) {
|
|
71
|
+
const said = new Map();
|
|
72
|
+
return {
|
|
73
|
+
update: (rows) => {
|
|
74
|
+
for (const row of rows) {
|
|
75
|
+
if (said.get(row.label) === row.detail) {
|
|
76
|
+
continue;
|
|
77
|
+
}
|
|
78
|
+
said.set(row.label, row.detail);
|
|
79
|
+
deps.write(`· ${row.label} — ${row.detail}\n`);
|
|
80
|
+
}
|
|
81
|
+
},
|
|
82
|
+
stop: () => { },
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
//# sourceMappingURL=progress.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"progress.js","sourceRoot":"","sources":["../../src/cli/progress.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAwBH,MAAM,CAAC,MAAM,OAAO,GAAG,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,CAAU,CAAC;AACnF,MAAM,CAAC,MAAM,QAAQ,GAAG,EAAE,CAAC;AAE3B,MAAM,WAAW,GAAG,WAAW,CAAC;AAChC,MAAM,WAAW,GAAG,WAAW,CAAC;AAChC,MAAM,UAAU,GAAG,SAAS,CAAC;AAE7B,MAAM,EAAE,GAAG,CAAC,KAAa,EAAU,EAAE,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,KAAK,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;AAE1E,MAAM,UAAU,cAAc,CAAC,IAAkB;IAC/C,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,cAAc,CAAC,IAAI,CAAC,CAAC;AAC5D,CAAC;AAED,SAAS,UAAU,CAAC,IAAkB;IACpC,IAAI,IAAI,GAAmB,EAAE,CAAC;IAC9B,IAAI,KAAK,GAAG,CAAC,CAAC;IACd,IAAI,KAAK,GAAG,CAAC,CAAC;IACd,IAAI,MAAM,GAAkB,IAAI,CAAC;IACjC,IAAI,OAAO,GAAG,KAAK,CAAC;IAEpB,SAAS,IAAI;QACX,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,OAAO,CAAC,KAAK,GAAG,OAAO,CAAC,MAAM,CAAC,IAAI,EAAE,IAAI,GAAG,CAAC,KAAK,MAAM,GAAG,CAAC,MAAM,GAAG,CAAC,CAAC;QAC1G,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,KAAK,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC;QAElD,MAAM,OAAO,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,GAAG,UAAU,GAAG,IAAI,IAAI,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACvE,MAAM,OAAO,GAAG,GAAG,UAAU,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;QAElD,IAAI,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,KAAK,CAAC,GAAG,OAAO,GAAG,OAAO,GAAG,EAAE,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;QAC7D,KAAK,GAAG,KAAK,CAAC,MAAM,CAAC;IACvB,CAAC;IAED,OAAO;QACL,MAAM,EAAE,CAAC,IAAI,EAAE,EAAE;YACf,IAAI,OAAO,IAAI,CAAC,IAAI,CAAC,MAAM,KAAK,CAAC,IAAI,KAAK,KAAK,CAAC,CAAC,EAAE,CAAC;gBAClD,OAAO;YACT,CAAC;YAED,IAAI,GAAG,IAAI,CAAC;YAEZ,IAAI,MAAM,KAAK,IAAI,EAAE,CAAC;gBACpB,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC;gBACxB,MAAM,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,EAAE;oBAC1B,KAAK,IAAI,CAAC,CAAC;oBACX,IAAI,EAAE,CAAC;gBACT,CAAC,EAAE,QAAQ,CAAC,CAAC;YACf,CAAC;YAED,IAAI,EAAE,CAAC;QACT,CAAC;QAED,IAAI,EAAE,GAAG,EAAE;YACT,IAAI,OAAO,EAAE,CAAC;gBACZ,OAAO;YACT,CAAC;YACD,OAAO,GAAG,IAAI,CAAC;YAEf,MAAM,EAAE,MAAM,EAAE,CAAC;YACjB,MAAM,GAAG,IAAI,CAAC;YAEd,IAAI,KAAK,GAAG,CAAC,EAAE,CAAC;gBACd,IAAI,GAAG,EAAE,CAAC;gBACV,IAAI,EAAE,CAAC;YACT,CAAC;YAED,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC;QAC1B,CAAC;KACF,CAAC;AACJ,CAAC;AAED;;;GAGG;AACH,SAAS,cAAc,CAAC,IAAkB;IACxC,MAAM,IAAI,GAAG,IAAI,GAAG,EAAkB,CAAC;IAEvC,OAAO;QACL,MAAM,EAAE,CAAC,IAAI,EAAE,EAAE;YACf,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;gBACvB,IAAI,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,GAAG,CAAC,MAAM,EAAE,CAAC;oBACvC,SAAS;gBACX,CAAC;gBACD,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,KAAK,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;gBAChC,IAAI,CAAC,KAAK,CAAC,KAAK,GAAG,CAAC,KAAK,MAAM,GAAG,CAAC,MAAM,IAAI,CAAC,CAAC;YACjD,CAAC;QACH,CAAC;QAED,IAAI,EAAE,GAAG,EAAE,GAAE,CAAC;KACf,CAAC;AACJ,CAAC"}
|
package/dist/cli/sync.js
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/** How long ago, in human terms — "never" when there is no date at all. */
|
|
2
|
+
export function relativeAgo(at, now) {
|
|
3
|
+
if (at === null) {
|
|
4
|
+
return "never";
|
|
5
|
+
}
|
|
6
|
+
const minutes = Math.floor((now.getTime() - at.getTime()) / 60_000);
|
|
7
|
+
if (minutes < 1) {
|
|
8
|
+
return "just now";
|
|
9
|
+
}
|
|
10
|
+
if (minutes < 60) {
|
|
11
|
+
return `${minutes}m ago`;
|
|
12
|
+
}
|
|
13
|
+
const hours = Math.floor(minutes / 60);
|
|
14
|
+
if (hours < 48) {
|
|
15
|
+
return `${hours}h ago`;
|
|
16
|
+
}
|
|
17
|
+
return `${Math.floor(hours / 24)}d ago`;
|
|
18
|
+
}
|
|
19
|
+
/** "synced 3h ago", or "never synced" when the connection has no `lastUpdatedAt` at all. */
|
|
20
|
+
export function describeSync(lastUpdatedAt, now) {
|
|
21
|
+
if (lastUpdatedAt === null) {
|
|
22
|
+
return "never synced";
|
|
23
|
+
}
|
|
24
|
+
return `synced ${relativeAgo(lastUpdatedAt, now)}`;
|
|
25
|
+
}
|
|
26
|
+
//# sourceMappingURL=sync.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sync.js","sourceRoot":"","sources":["../../src/cli/sync.ts"],"names":[],"mappings":"AAAA,2EAA2E;AAC3E,MAAM,UAAU,WAAW,CAAC,EAAe,EAAE,GAAS;IACpD,IAAI,EAAE,KAAK,IAAI,EAAE,CAAC;QAChB,OAAO,OAAO,CAAC;IACjB,CAAC;IAED,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC,OAAO,EAAE,GAAG,EAAE,CAAC,OAAO,EAAE,CAAC,GAAG,MAAM,CAAC,CAAC;IACpE,IAAI,OAAO,GAAG,CAAC,EAAE,CAAC;QAChB,OAAO,UAAU,CAAC;IACpB,CAAC;IACD,IAAI,OAAO,GAAG,EAAE,EAAE,CAAC;QACjB,OAAO,GAAG,OAAO,OAAO,CAAC;IAC3B,CAAC;IAED,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,GAAG,EAAE,CAAC,CAAC;IACvC,IAAI,KAAK,GAAG,EAAE,EAAE,CAAC;QACf,OAAO,GAAG,KAAK,OAAO,CAAC;IACzB,CAAC;IACD,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,GAAG,EAAE,CAAC,OAAO,CAAC;AAC1C,CAAC;AAED,4FAA4F;AAC5F,MAAM,UAAU,YAAY,CAAC,aAA0B,EAAE,GAAS;IAChE,IAAI,aAAa,KAAK,IAAI,EAAE,CAAC;QAC3B,OAAO,cAAc,CAAC;IACxB,CAAC;IACD,OAAO,UAAU,WAAW,CAAC,aAAa,EAAE,GAAG,CAAC,EAAE,CAAC;AACrD,CAAC"}
|
package/dist/config.js
ADDED
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import { isAbsolute, join } from "node:path";
|
|
2
|
+
/**
|
|
3
|
+
* Configuration comes from the environment and nowhere else (ADR §4, amended
|
|
4
|
+
* 2026-07-25). Environment *variables*, never a `.env` file: §16.2 records the
|
|
5
|
+
* prior implementation auto-loading one from the current working directory,
|
|
6
|
+
* which under `npx` is wherever the user happened to be standing.
|
|
7
|
+
*/
|
|
8
|
+
const APP_DIR = "cata-centavo";
|
|
9
|
+
/**
|
|
10
|
+
* 8-4-4-4-12 hex, deliberately looser than a real UUID check. The point is to
|
|
11
|
+
* catch a truncated or mis-pasted id before it costs a network round trip, not
|
|
12
|
+
* to assert a UUID version we have never verified Pluggy emits.
|
|
13
|
+
*/
|
|
14
|
+
const ITEM_ID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
15
|
+
const HOW_TO_SET = 'Export it in your shell, or declare it in the "env" block of your MCP client config — a client that spawns this server does not read your shell profile.';
|
|
16
|
+
/**
|
|
17
|
+
* Validates the environment into a `Config`, collecting every problem rather
|
|
18
|
+
* than stopping at the first — a user missing two variables should learn that
|
|
19
|
+
* in one run.
|
|
20
|
+
*
|
|
21
|
+
* Failure is a return value. Nothing here throws and nothing calls
|
|
22
|
+
* `process.exit`: `init` and `doctor` exist to report exactly these conditions
|
|
23
|
+
* and cannot report one that kills the process (ADR §16.2).
|
|
24
|
+
*
|
|
25
|
+
* Hand-written rather than a Zod schema because the messages *are* the feature
|
|
26
|
+
* here (§14.6), and Zod's generic issue text would have to be replaced anyway.
|
|
27
|
+
* Zod earns its keep on the wire bodies in `pluggy/wire.ts`, where the shape is
|
|
28
|
+
* deep and the inference matters.
|
|
29
|
+
*/
|
|
30
|
+
export function loadConfig(env) {
|
|
31
|
+
const problems = [];
|
|
32
|
+
const clientId = readRequired(env, "PLUGGY_CLIENT_ID", problems);
|
|
33
|
+
const clientSecret = readRequired(env, "PLUGGY_CLIENT_SECRET", problems);
|
|
34
|
+
const itemIds = readItemIds(env, problems);
|
|
35
|
+
if (clientId === undefined || clientSecret === undefined || problems.length > 0) {
|
|
36
|
+
return { ok: false, problems };
|
|
37
|
+
}
|
|
38
|
+
return { ok: true, config: { credentials: { clientId, clientSecret }, itemIds } };
|
|
39
|
+
}
|
|
40
|
+
function readRequired(env, name, problems) {
|
|
41
|
+
const value = env[name]?.trim();
|
|
42
|
+
if (value === undefined || value === "") {
|
|
43
|
+
problems.push(`${name} is missing or empty. ${HOW_TO_SET}`);
|
|
44
|
+
return undefined;
|
|
45
|
+
}
|
|
46
|
+
return value;
|
|
47
|
+
}
|
|
48
|
+
function readItemIds(env, problems) {
|
|
49
|
+
const name = "PLUGGY_ITEM_IDS";
|
|
50
|
+
const raw = env[name]?.trim();
|
|
51
|
+
if (raw === undefined || raw === "") {
|
|
52
|
+
problems.push(`${name} is missing or empty. It holds the connection ids from your MeuPluggy dashboard, separated by commas. ${HOW_TO_SET}`);
|
|
53
|
+
return [];
|
|
54
|
+
}
|
|
55
|
+
const ids = raw
|
|
56
|
+
.split(",")
|
|
57
|
+
.map((id) => id.trim())
|
|
58
|
+
.filter((id) => id !== "");
|
|
59
|
+
if (ids.length === 0) {
|
|
60
|
+
problems.push(`${name} lists no ids, only separators.`);
|
|
61
|
+
return [];
|
|
62
|
+
}
|
|
63
|
+
problems.push(...idProblems(name, ids));
|
|
64
|
+
return ids;
|
|
65
|
+
}
|
|
66
|
+
/** Both problems are reported, because a list can be malformed *and* repeat itself. */
|
|
67
|
+
function idProblems(name, ids) {
|
|
68
|
+
const problems = [];
|
|
69
|
+
const malformed = ids.filter((id) => !ITEM_ID.test(id));
|
|
70
|
+
if (malformed.length > 0) {
|
|
71
|
+
problems.push(`${name} has ${malformed.length} entry that is not a connection id: ${malformed.join(", ")}`);
|
|
72
|
+
}
|
|
73
|
+
const duplicates = [...new Set(ids.filter((id, index) => ids.indexOf(id) !== index))];
|
|
74
|
+
if (duplicates.length > 0) {
|
|
75
|
+
problems.push(`${name} lists a duplicate id: ${duplicates.join(", ")}`);
|
|
76
|
+
}
|
|
77
|
+
return problems;
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* The two files of ADR §10. `npx` runs from an arbitrary directory, so nothing
|
|
81
|
+
* lives in the cwd.
|
|
82
|
+
*
|
|
83
|
+
* `cache.db` is droppable and rebuilt from Pluggy; `data.db` holds work the
|
|
84
|
+
* user cannot get back. They are deliberately in different XDG roots so an `rm`
|
|
85
|
+
* aimed at the cache cannot reach the other one.
|
|
86
|
+
*/
|
|
87
|
+
export function resolvePaths(env, system) {
|
|
88
|
+
const darwin = system.platform === "darwin";
|
|
89
|
+
let cacheFallback;
|
|
90
|
+
let dataFallback;
|
|
91
|
+
let stateFallback;
|
|
92
|
+
if (darwin) {
|
|
93
|
+
cacheFallback = join(system.home, "Library", "Caches");
|
|
94
|
+
dataFallback = join(system.home, "Library", "Application Support");
|
|
95
|
+
stateFallback = join(system.home, "Library", "Logs");
|
|
96
|
+
}
|
|
97
|
+
else {
|
|
98
|
+
cacheFallback = join(system.home, ".cache");
|
|
99
|
+
dataFallback = join(system.home, ".local", "share");
|
|
100
|
+
stateFallback = join(system.home, ".local", "state");
|
|
101
|
+
}
|
|
102
|
+
const cacheHome = xdgHome(env, "XDG_CACHE_HOME", cacheFallback);
|
|
103
|
+
const dataHome = xdgHome(env, "XDG_DATA_HOME", dataFallback);
|
|
104
|
+
const stateHome = xdgHome(env, "XDG_STATE_HOME", stateFallback);
|
|
105
|
+
return {
|
|
106
|
+
cacheDb: join(cacheHome, APP_DIR, "cache.db"),
|
|
107
|
+
dataDb: join(dataHome, APP_DIR, "data.db"),
|
|
108
|
+
logFile: join(stateHome, APP_DIR, "cata-centavo.log"),
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
/** The XDG spec says a relative path in one of these variables must be ignored. */
|
|
112
|
+
function xdgHome(env, name, fallback) {
|
|
113
|
+
const value = env[name];
|
|
114
|
+
if (value !== undefined && isAbsolute(value)) {
|
|
115
|
+
return value;
|
|
116
|
+
}
|
|
117
|
+
return fallback;
|
|
118
|
+
}
|
|
119
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* An account as we speak of it, which is not as Pluggy speaks of it (ADR §14.1).
|
|
3
|
+
* `type` is a superset of Pluggy's `BANK | CREDIT` because investments and loans
|
|
4
|
+
* are representable under neither; `pluggy/mapper.ts` owns the widening.
|
|
5
|
+
*/
|
|
6
|
+
export const ACCOUNT_TYPES = {
|
|
7
|
+
bank: "BANK",
|
|
8
|
+
credit: "CREDIT",
|
|
9
|
+
investment: "INVESTMENT",
|
|
10
|
+
loan: "LOAN",
|
|
11
|
+
};
|
|
12
|
+
//# sourceMappingURL=account.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"account.js","sourceRoot":"","sources":["../../src/core/account.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,MAAM,CAAC,MAAM,aAAa,GAAG;IAC3B,IAAI,EAAE,MAAM;IACZ,MAAM,EAAE,QAAQ;IAChB,UAAU,EAAE,YAAY;IACxB,IAAI,EAAE,MAAM;CACJ,CAAC"}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { consentState } from "./consent.js";
|
|
2
|
+
/**
|
|
3
|
+
* Collects accounts from every configured connection concurrently.
|
|
4
|
+
*
|
|
5
|
+
* A rejected request and a successful empty response both leave the connection
|
|
6
|
+
* unavailable so callers never mistake partial coverage for complete data.
|
|
7
|
+
*/
|
|
8
|
+
export async function collectAccounts(bank, connectionIds, toFailure, clock) {
|
|
9
|
+
const requests = connectionIds.map((connectionId) => ({
|
|
10
|
+
connectionId,
|
|
11
|
+
accounts: bank.getAccounts(connectionId),
|
|
12
|
+
}));
|
|
13
|
+
const settled = await Promise.allSettled(requests.map(({ accounts }) => accounts));
|
|
14
|
+
const accounts = [];
|
|
15
|
+
const unavailable = [];
|
|
16
|
+
for (const [index, result] of settled.entries()) {
|
|
17
|
+
const { connectionId } = requests[index];
|
|
18
|
+
if (result.status === "rejected") {
|
|
19
|
+
unavailable.push({ connectionId, ...toFailure(result.reason) });
|
|
20
|
+
continue;
|
|
21
|
+
}
|
|
22
|
+
if (result.value.length === 0) {
|
|
23
|
+
unavailable.push(await diagnoseEmpty(bank, connectionId, clock));
|
|
24
|
+
continue;
|
|
25
|
+
}
|
|
26
|
+
accounts.push(...result.value);
|
|
27
|
+
}
|
|
28
|
+
return { accounts, unavailable };
|
|
29
|
+
}
|
|
30
|
+
function noAccountsFailure(connectionId) {
|
|
31
|
+
return {
|
|
32
|
+
connectionId,
|
|
33
|
+
kind: "no-accounts",
|
|
34
|
+
message: `Connection ${connectionId} answered but returned no accounts.`,
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
function dateOnly(date) {
|
|
38
|
+
return date.toISOString().slice(0, 10);
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Why an empty account list came back. A diagnostic query that itself fails
|
|
42
|
+
* must not turn a mild "no accounts" into a crash — it exists to explain, not
|
|
43
|
+
* to bring the call down — so any error from `getConsent` falls back to the
|
|
44
|
+
* same `no-accounts` result a connection with no observable consent gets.
|
|
45
|
+
*/
|
|
46
|
+
async function diagnoseEmpty(bank, connectionId, clock) {
|
|
47
|
+
let consent;
|
|
48
|
+
try {
|
|
49
|
+
consent = await bank.getConsent(connectionId);
|
|
50
|
+
}
|
|
51
|
+
catch {
|
|
52
|
+
return noAccountsFailure(connectionId);
|
|
53
|
+
}
|
|
54
|
+
return consentFailure(connectionId, consent, consentState(consent, clock.now()));
|
|
55
|
+
}
|
|
56
|
+
function consentFailure(connectionId, consent, state) {
|
|
57
|
+
if (state === "revoked") {
|
|
58
|
+
return revokedFailure(connectionId, dateField(consent, "revokedAt"));
|
|
59
|
+
}
|
|
60
|
+
if (state === "expired") {
|
|
61
|
+
return expiredFailure(connectionId, dateField(consent, "expiresAt"));
|
|
62
|
+
}
|
|
63
|
+
return noAccountsFailure(connectionId);
|
|
64
|
+
}
|
|
65
|
+
function dateField(consent, field) {
|
|
66
|
+
if (consent === null) {
|
|
67
|
+
return null;
|
|
68
|
+
}
|
|
69
|
+
return consent[field];
|
|
70
|
+
}
|
|
71
|
+
function revokedFailure(connectionId, revokedAt) {
|
|
72
|
+
if (revokedAt === null) {
|
|
73
|
+
return noAccountsFailure(connectionId);
|
|
74
|
+
}
|
|
75
|
+
return {
|
|
76
|
+
connectionId,
|
|
77
|
+
kind: "consent-revoked",
|
|
78
|
+
message: `Connection ${connectionId}'s consent was revoked on ${dateOnly(revokedAt)}; re-link this connection to restore access.`,
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
function expiredFailure(connectionId, expiresAt) {
|
|
82
|
+
if (expiresAt === null) {
|
|
83
|
+
return noAccountsFailure(connectionId);
|
|
84
|
+
}
|
|
85
|
+
return {
|
|
86
|
+
connectionId,
|
|
87
|
+
kind: "consent-expired",
|
|
88
|
+
message: `Connection ${connectionId}'s consent expired on ${dateOnly(expiresAt)}.`,
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
//# sourceMappingURL=accounts.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"accounts.js","sourceRoot":"","sources":["../../src/core/accounts.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAqB,MAAM,cAAc,CAAC;AAc/D;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,eAAe,CACnC,IAAU,EACV,aAAgC,EAChC,SAA0C,EAC1C,KAAY;IAEZ,MAAM,QAAQ,GAAG,aAAa,CAAC,GAAG,CAAC,CAAC,YAAY,EAAE,EAAE,CAAC,CAAC;QACpD,YAAY;QACZ,QAAQ,EAAE,IAAI,CAAC,WAAW,CAAC,YAAY,CAAC;KACzC,CAAC,CAAC,CAAC;IACJ,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,UAAU,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,EAAE,QAAQ,EAAE,EAAE,EAAE,CAAC,QAAQ,CAAC,CAAC,CAAC;IACnF,MAAM,QAAQ,GAAc,EAAE,CAAC;IAC/B,MAAM,WAAW,GAA4B,EAAE,CAAC;IAEhD,KAAK,MAAM,CAAC,KAAK,EAAE,MAAM,CAAC,IAAI,OAAO,CAAC,OAAO,EAAE,EAAE,CAAC;QAChD,MAAM,EAAE,YAAY,EAAE,GAAG,QAAQ,CAAC,KAAK,CAAE,CAAC;QAE1C,IAAI,MAAM,CAAC,MAAM,KAAK,UAAU,EAAE,CAAC;YACjC,WAAW,CAAC,IAAI,CAAC,EAAE,YAAY,EAAE,GAAG,SAAS,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;YAChE,SAAS;QACX,CAAC;QAED,IAAI,MAAM,CAAC,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC9B,WAAW,CAAC,IAAI,CAAC,MAAM,aAAa,CAAC,IAAI,EAAE,YAAY,EAAE,KAAK,CAAC,CAAC,CAAC;YACjE,SAAS;QACX,CAAC;QAED,QAAQ,CAAC,IAAI,CAAC,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC;IACjC,CAAC;IAED,OAAO,EAAE,QAAQ,EAAE,WAAW,EAAE,CAAC;AACnC,CAAC;AAED,SAAS,iBAAiB,CAAC,YAAoB;IAC7C,OAAO;QACL,YAAY;QACZ,IAAI,EAAE,aAAa;QACnB,OAAO,EAAE,cAAc,YAAY,qCAAqC;KACzE,CAAC;AACJ,CAAC;AAED,SAAS,QAAQ,CAAC,IAAU;IAC1B,OAAO,IAAI,CAAC,WAAW,EAAE,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;AACzC,CAAC;AAED;;;;;GAKG;AACH,KAAK,UAAU,aAAa,CAAC,IAAU,EAAE,YAAoB,EAAE,KAAY;IACzE,IAAI,OAAuB,CAAC;IAC5B,IAAI,CAAC;QACH,OAAO,GAAG,MAAM,IAAI,CAAC,UAAU,CAAC,YAAY,CAAC,CAAC;IAChD,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,iBAAiB,CAAC,YAAY,CAAC,CAAC;IACzC,CAAC;IAED,OAAO,cAAc,CAAC,YAAY,EAAE,OAAO,EAAE,YAAY,CAAC,OAAO,EAAE,KAAK,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC;AACnF,CAAC;AAED,SAAS,cAAc,CAAC,YAAoB,EAAE,OAAuB,EAAE,KAAmB;IACxF,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;QACxB,OAAO,cAAc,CAAC,YAAY,EAAE,SAAS,CAAC,OAAO,EAAE,WAAW,CAAC,CAAC,CAAC;IACvE,CAAC;IAED,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;QACxB,OAAO,cAAc,CAAC,YAAY,EAAE,SAAS,CAAC,OAAO,EAAE,WAAW,CAAC,CAAC,CAAC;IACvE,CAAC;IAED,OAAO,iBAAiB,CAAC,YAAY,CAAC,CAAC;AACzC,CAAC;AAED,SAAS,SAAS,CAAC,OAAuB,EAAE,KAAgC;IAC1E,IAAI,OAAO,KAAK,IAAI,EAAE,CAAC;QACrB,OAAO,IAAI,CAAC;IACd,CAAC;IACD,OAAO,OAAO,CAAC,KAAK,CAAC,CAAC;AACxB,CAAC;AAED,SAAS,cAAc,CAAC,YAAoB,EAAE,SAAsB;IAClE,IAAI,SAAS,KAAK,IAAI,EAAE,CAAC;QACvB,OAAO,iBAAiB,CAAC,YAAY,CAAC,CAAC;IACzC,CAAC;IACD,OAAO;QACL,YAAY;QACZ,IAAI,EAAE,iBAAiB;QACvB,OAAO,EAAE,cAAc,YAAY,6BAA6B,QAAQ,CAAC,SAAS,CAAC,8CAA8C;KAClI,CAAC;AACJ,CAAC;AAED,SAAS,cAAc,CAAC,YAAoB,EAAE,SAAsB;IAClE,IAAI,SAAS,KAAK,IAAI,EAAE,CAAC;QACvB,OAAO,iBAAiB,CAAC,YAAY,CAAC,CAAC;IACzC,CAAC;IACD,OAAO;QACL,YAAY;QACZ,IAAI,EAAE,iBAAiB;QACvB,OAAO,EAAE,cAAc,YAAY,yBAAyB,QAAQ,CAAC,SAAS,CAAC,GAAG;KACnF,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { isSelfTransfer } from "./self-transfer.js";
|
|
2
|
+
/**
|
|
3
|
+
* Groups by the category the derivation resolved, not by the leaf Pluggy sent.
|
|
4
|
+
*
|
|
5
|
+
* Those are the same answer only while the enrichment is alive. Rolling the raw
|
|
6
|
+
* leaf up here instead would make a manual override invisible to the totals,
|
|
7
|
+
* and would report a whole wallet as uncategorized the day the plan drops to
|
|
8
|
+
* free — while the `categories` filter, which resolves the same rows in SQL,
|
|
9
|
+
* kept returning them. Two totals for one question is the failure the PRD's
|
|
10
|
+
* first rule names.
|
|
11
|
+
*/
|
|
12
|
+
export function aggregate(rows, today) {
|
|
13
|
+
const groups = new Map();
|
|
14
|
+
const totals = { spentCents: 0, receivedCents: 0, upcomingCents: 0, upcomingCount: 0 };
|
|
15
|
+
for (const row of rows) {
|
|
16
|
+
addToGroup(groups, row);
|
|
17
|
+
addToTotals(totals, row, today);
|
|
18
|
+
}
|
|
19
|
+
return {
|
|
20
|
+
groups: [...groups.values()].map(toCategoryGroup).sort(compareGroups),
|
|
21
|
+
spentCents: totals.spentCents,
|
|
22
|
+
receivedCents: totals.receivedCents,
|
|
23
|
+
upcoming: { totalCents: totals.upcomingCents, count: totals.upcomingCount },
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
function addToGroup(groups, row) {
|
|
27
|
+
const categoryId = row.category;
|
|
28
|
+
let group = groups.get(categoryId);
|
|
29
|
+
if (group === undefined) {
|
|
30
|
+
group = { categoryId, totalCents: 0, count: 0, rows: [] };
|
|
31
|
+
groups.set(categoryId, group);
|
|
32
|
+
}
|
|
33
|
+
group.totalCents += row.amountCents;
|
|
34
|
+
group.count += 1;
|
|
35
|
+
group.rows.push(row);
|
|
36
|
+
}
|
|
37
|
+
function addToTotals(totals, row, today) {
|
|
38
|
+
if (row.localDate > today) {
|
|
39
|
+
totals.upcomingCents += row.amountCents;
|
|
40
|
+
totals.upcomingCount += 1;
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
if (isSelfTransfer(row)) {
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
if (row.amountCents < 0) {
|
|
47
|
+
totals.spentCents += -row.amountCents;
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
if (row.amountCents > 0) {
|
|
51
|
+
totals.receivedCents += row.amountCents;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
function toCategoryGroup(group) {
|
|
55
|
+
const sampleRows = [...group.rows].sort(compareSampleRows).slice(0, 10);
|
|
56
|
+
return {
|
|
57
|
+
categoryId: group.categoryId,
|
|
58
|
+
totalCents: group.totalCents,
|
|
59
|
+
count: group.count,
|
|
60
|
+
sampleIds: sampleRows.map((row) => row.id),
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
function compareSampleRows(left, right) {
|
|
64
|
+
const amountDifference = Math.abs(right.amountCents) - Math.abs(left.amountCents);
|
|
65
|
+
if (amountDifference !== 0) {
|
|
66
|
+
return amountDifference;
|
|
67
|
+
}
|
|
68
|
+
return left.id.localeCompare(right.id);
|
|
69
|
+
}
|
|
70
|
+
function compareGroups(left, right) {
|
|
71
|
+
const totalDifference = Math.abs(right.totalCents) - Math.abs(left.totalCents);
|
|
72
|
+
if (totalDifference !== 0) {
|
|
73
|
+
return totalDifference;
|
|
74
|
+
}
|
|
75
|
+
return groupKey(left.categoryId).localeCompare(groupKey(right.categoryId));
|
|
76
|
+
}
|
|
77
|
+
function groupKey(categoryId) {
|
|
78
|
+
if (categoryId === null) {
|
|
79
|
+
return "";
|
|
80
|
+
}
|
|
81
|
+
return categoryId;
|
|
82
|
+
}
|
|
83
|
+
//# sourceMappingURL=aggregate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"aggregate.js","sourceRoot":"","sources":["../../src/core/aggregate.ts"],"names":[],"mappings":"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"}
|