@firela/billclaw-core 0.1.3
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 +109 -0
- package/dist/billclaw.d.ts +76 -0
- package/dist/billclaw.d.ts.map +1 -0
- package/dist/billclaw.js +205 -0
- package/dist/billclaw.js.map +1 -0
- package/dist/credentials/index.d.ts +8 -0
- package/dist/credentials/index.d.ts.map +1 -0
- package/dist/credentials/index.js +8 -0
- package/dist/credentials/index.js.map +1 -0
- package/dist/credentials/keychain.d.ts +92 -0
- package/dist/credentials/keychain.d.ts.map +1 -0
- package/dist/credentials/keychain.js +172 -0
- package/dist/credentials/keychain.js.map +1 -0
- package/dist/credentials/store.d.ts +76 -0
- package/dist/credentials/store.d.ts.map +1 -0
- package/dist/credentials/store.js +144 -0
- package/dist/credentials/store.js.map +1 -0
- package/dist/errors/errors.d.ts +92 -0
- package/dist/errors/errors.d.ts.map +1 -0
- package/dist/errors/errors.js +315 -0
- package/dist/errors/errors.js.map +1 -0
- package/dist/errors/index.d.ts +7 -0
- package/dist/errors/index.d.ts.map +1 -0
- package/dist/errors/index.js +7 -0
- package/dist/errors/index.js.map +1 -0
- package/dist/exporters/beancount.d.ts +42 -0
- package/dist/exporters/beancount.d.ts.map +1 -0
- package/dist/exporters/beancount.js +141 -0
- package/dist/exporters/beancount.js.map +1 -0
- package/dist/exporters/index.d.ts +8 -0
- package/dist/exporters/index.d.ts.map +1 -0
- package/dist/exporters/index.js +8 -0
- package/dist/exporters/index.js.map +1 -0
- package/dist/exporters/ledger.d.ts +42 -0
- package/dist/exporters/ledger.d.ts.map +1 -0
- package/dist/exporters/ledger.js +139 -0
- package/dist/exporters/ledger.js.map +1 -0
- package/dist/index.d.ts +23 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +34 -0
- package/dist/index.js.map +1 -0
- package/dist/models/config.d.ts +552 -0
- package/dist/models/config.d.ts.map +1 -0
- package/dist/models/config.js +168 -0
- package/dist/models/config.js.map +1 -0
- package/dist/models/index.d.ts +7 -0
- package/dist/models/index.d.ts.map +1 -0
- package/dist/models/index.js +8 -0
- package/dist/models/index.js.map +1 -0
- package/dist/runtime/index.d.ts +7 -0
- package/dist/runtime/index.d.ts.map +1 -0
- package/dist/runtime/index.js +7 -0
- package/dist/runtime/index.js.map +1 -0
- package/dist/runtime/types.d.ts +110 -0
- package/dist/runtime/types.d.ts.map +1 -0
- package/dist/runtime/types.js +85 -0
- package/dist/runtime/types.js.map +1 -0
- package/dist/security/audit.d.ts +148 -0
- package/dist/security/audit.d.ts.map +1 -0
- package/dist/security/audit.js +286 -0
- package/dist/security/audit.js.map +1 -0
- package/dist/security/index.d.ts +7 -0
- package/dist/security/index.d.ts.map +1 -0
- package/dist/security/index.js +7 -0
- package/dist/security/index.js.map +1 -0
- package/dist/services/event-emitter.d.ts +171 -0
- package/dist/services/event-emitter.d.ts.map +1 -0
- package/dist/services/event-emitter.js +287 -0
- package/dist/services/event-emitter.js.map +1 -0
- package/dist/services/index.d.ts +8 -0
- package/dist/services/index.d.ts.map +1 -0
- package/dist/services/index.js +8 -0
- package/dist/services/index.js.map +1 -0
- package/dist/sources/gmail/bill-recognizer.d.ts +71 -0
- package/dist/sources/gmail/bill-recognizer.d.ts.map +1 -0
- package/dist/sources/gmail/bill-recognizer.js +341 -0
- package/dist/sources/gmail/bill-recognizer.js.map +1 -0
- package/dist/sources/gmail/email-parser.d.ts +68 -0
- package/dist/sources/gmail/email-parser.d.ts.map +1 -0
- package/dist/sources/gmail/email-parser.js +238 -0
- package/dist/sources/gmail/email-parser.js.map +1 -0
- package/dist/sources/gmail/gmail-fetch.d.ts +54 -0
- package/dist/sources/gmail/gmail-fetch.d.ts.map +1 -0
- package/dist/sources/gmail/gmail-fetch.js +300 -0
- package/dist/sources/gmail/gmail-fetch.js.map +1 -0
- package/dist/sources/gmail/index.d.ts +7 -0
- package/dist/sources/gmail/index.d.ts.map +1 -0
- package/dist/sources/gmail/index.js +7 -0
- package/dist/sources/gmail/index.js.map +1 -0
- package/dist/sources/index.d.ts +8 -0
- package/dist/sources/index.d.ts.map +1 -0
- package/dist/sources/index.js +8 -0
- package/dist/sources/index.js.map +1 -0
- package/dist/sources/plaid/index.d.ts +7 -0
- package/dist/sources/plaid/index.d.ts.map +1 -0
- package/dist/sources/plaid/index.js +7 -0
- package/dist/sources/plaid/index.js.map +1 -0
- package/dist/sources/plaid/plaid-sync.d.ts +42 -0
- package/dist/sources/plaid/plaid-sync.d.ts.map +1 -0
- package/dist/sources/plaid/plaid-sync.js +182 -0
- package/dist/sources/plaid/plaid-sync.js.map +1 -0
- package/dist/storage/cache.d.ts +134 -0
- package/dist/storage/cache.d.ts.map +1 -0
- package/dist/storage/cache.js +239 -0
- package/dist/storage/cache.js.map +1 -0
- package/dist/storage/index.d.ts +11 -0
- package/dist/storage/index.d.ts.map +1 -0
- package/dist/storage/index.js +11 -0
- package/dist/storage/index.js.map +1 -0
- package/dist/storage/indexes.d.ts +136 -0
- package/dist/storage/indexes.d.ts.map +1 -0
- package/dist/storage/indexes.js +294 -0
- package/dist/storage/indexes.js.map +1 -0
- package/dist/storage/locking.d.ts +103 -0
- package/dist/storage/locking.d.ts.map +1 -0
- package/dist/storage/locking.js +158 -0
- package/dist/storage/locking.js.map +1 -0
- package/dist/storage/streaming.d.ts +102 -0
- package/dist/storage/streaming.d.ts.map +1 -0
- package/dist/storage/streaming.js +245 -0
- package/dist/storage/streaming.js.map +1 -0
- package/dist/storage/transaction-storage.d.ts +101 -0
- package/dist/storage/transaction-storage.d.ts.map +1 -0
- package/dist/storage/transaction-storage.js +193 -0
- package/dist/storage/transaction-storage.js.map +1 -0
- package/dist/sync/index.d.ts +7 -0
- package/dist/sync/index.d.ts.map +1 -0
- package/dist/sync/index.js +7 -0
- package/dist/sync/index.js.map +1 -0
- package/dist/sync/sync-service.d.ts +42 -0
- package/dist/sync/sync-service.d.ts.map +1 -0
- package/dist/sync/sync-service.js +112 -0
- package/dist/sync/sync-service.js.map +1 -0
- package/dist/test-fixtures.d.ts +38 -0
- package/dist/test-fixtures.d.ts.map +1 -0
- package/dist/test-fixtures.js +137 -0
- package/dist/test-fixtures.js.map +1 -0
- package/package.json +68 -0
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Local file storage utilities for BillClaw data
|
|
3
|
+
*/
|
|
4
|
+
import * as fs from "node:fs/promises";
|
|
5
|
+
import * as path from "node:path";
|
|
6
|
+
import * as os from "node:os";
|
|
7
|
+
/**
|
|
8
|
+
* Get the base storage directory
|
|
9
|
+
*/
|
|
10
|
+
export async function getStorageDir(config) {
|
|
11
|
+
const storagePath = config?.path || "~/.billclaw";
|
|
12
|
+
const expandedPath = storagePath.replace(/^~/, os.homedir());
|
|
13
|
+
return expandedPath;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Initialize storage directory structure
|
|
17
|
+
*/
|
|
18
|
+
export async function initializeStorage(config) {
|
|
19
|
+
const baseDir = await getStorageDir(config);
|
|
20
|
+
const directories = [
|
|
21
|
+
baseDir,
|
|
22
|
+
path.join(baseDir, "accounts"),
|
|
23
|
+
path.join(baseDir, "transactions"),
|
|
24
|
+
path.join(baseDir, "sync"),
|
|
25
|
+
];
|
|
26
|
+
for (const dir of directories) {
|
|
27
|
+
try {
|
|
28
|
+
await fs.mkdir(dir, { recursive: true });
|
|
29
|
+
}
|
|
30
|
+
catch {
|
|
31
|
+
// Directory may already exist
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Read account registry
|
|
37
|
+
*/
|
|
38
|
+
export async function readAccountRegistry(config) {
|
|
39
|
+
const baseDir = await getStorageDir(config);
|
|
40
|
+
const filePath = path.join(baseDir, "accounts.json");
|
|
41
|
+
try {
|
|
42
|
+
const content = await fs.readFile(filePath, "utf-8");
|
|
43
|
+
return JSON.parse(content);
|
|
44
|
+
}
|
|
45
|
+
catch {
|
|
46
|
+
// File doesn't exist yet
|
|
47
|
+
return [];
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Write account registry
|
|
52
|
+
*/
|
|
53
|
+
export async function writeAccountRegistry(accounts, config) {
|
|
54
|
+
const baseDir = await getStorageDir(config);
|
|
55
|
+
const filePath = path.join(baseDir, "accounts.json");
|
|
56
|
+
await initializeStorage(config);
|
|
57
|
+
await fs.writeFile(filePath, JSON.stringify(accounts, null, 2), "utf-8");
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Read transactions for an account and month
|
|
61
|
+
*/
|
|
62
|
+
export async function readTransactions(accountId, year, month, config) {
|
|
63
|
+
const baseDir = await getStorageDir(config);
|
|
64
|
+
const monthStr = month.toString().padStart(2, "0");
|
|
65
|
+
const filePath = path.join(baseDir, "transactions", accountId, `${year}`, `${monthStr}.json`);
|
|
66
|
+
try {
|
|
67
|
+
const content = await fs.readFile(filePath, "utf-8");
|
|
68
|
+
return JSON.parse(content);
|
|
69
|
+
}
|
|
70
|
+
catch {
|
|
71
|
+
return [];
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Write transactions for an account and month
|
|
76
|
+
* Uses atomic write (temp file + rename) for safety
|
|
77
|
+
*/
|
|
78
|
+
export async function writeTransactions(accountId, year, month, transactions, config) {
|
|
79
|
+
const baseDir = await getStorageDir(config);
|
|
80
|
+
const monthStr = month.toString().padStart(2, "0");
|
|
81
|
+
const dirPath = path.join(baseDir, "transactions", accountId, `${year}`);
|
|
82
|
+
const filePath = path.join(dirPath, `${monthStr}.json`);
|
|
83
|
+
await fs.mkdir(dirPath, { recursive: true });
|
|
84
|
+
// Atomic write: write to temp file first, then rename
|
|
85
|
+
const tempPath = filePath + ".tmp";
|
|
86
|
+
await fs.writeFile(tempPath, JSON.stringify(transactions, null, 2), "utf-8");
|
|
87
|
+
await fs.rename(tempPath, filePath);
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Append transactions to existing month file (with deduplication)
|
|
91
|
+
*/
|
|
92
|
+
export async function appendTransactions(accountId, year, month, newTransactions, config) {
|
|
93
|
+
const existing = await readTransactions(accountId, year, month, config);
|
|
94
|
+
const existingIds = new Set(existing.map((t) => t.transactionId));
|
|
95
|
+
let added = 0;
|
|
96
|
+
let updated = 0;
|
|
97
|
+
for (const txn of newTransactions) {
|
|
98
|
+
if (existingIds.has(txn.transactionId)) {
|
|
99
|
+
// Update existing transaction
|
|
100
|
+
const index = existing.findIndex((t) => t.transactionId === txn.transactionId);
|
|
101
|
+
if (index !== -1) {
|
|
102
|
+
existing[index] = txn;
|
|
103
|
+
updated++;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
else {
|
|
107
|
+
// Add new transaction
|
|
108
|
+
existing.push(txn);
|
|
109
|
+
existingIds.add(txn.transactionId);
|
|
110
|
+
added++;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
// Sort by date descending
|
|
114
|
+
existing.sort((a, b) => b.date.localeCompare(a.date));
|
|
115
|
+
await writeTransactions(accountId, year, month, existing, config);
|
|
116
|
+
return { added, updated };
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Read sync state for an account
|
|
120
|
+
*/
|
|
121
|
+
export async function readSyncStates(accountId, config) {
|
|
122
|
+
const baseDir = await getStorageDir(config);
|
|
123
|
+
const dirPath = path.join(baseDir, "sync", accountId);
|
|
124
|
+
try {
|
|
125
|
+
const files = await fs.readdir(dirPath);
|
|
126
|
+
const states = [];
|
|
127
|
+
for (const file of files) {
|
|
128
|
+
if (!file.endsWith(".json"))
|
|
129
|
+
continue;
|
|
130
|
+
const filePath = path.join(dirPath, file);
|
|
131
|
+
const content = await fs.readFile(filePath, "utf-8");
|
|
132
|
+
states.push(JSON.parse(content));
|
|
133
|
+
}
|
|
134
|
+
return states.sort((a, b) => b.startedAt.localeCompare(a.startedAt));
|
|
135
|
+
}
|
|
136
|
+
catch {
|
|
137
|
+
return [];
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Write sync state
|
|
142
|
+
*/
|
|
143
|
+
export async function writeSyncState(state, config) {
|
|
144
|
+
const baseDir = await getStorageDir(config);
|
|
145
|
+
const dirPath = path.join(baseDir, "sync", state.accountId);
|
|
146
|
+
const filePath = path.join(dirPath, `${state.syncId}.json`);
|
|
147
|
+
await fs.mkdir(dirPath, { recursive: true });
|
|
148
|
+
await fs.writeFile(filePath, JSON.stringify(state, null, 2), "utf-8");
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Read global cursor
|
|
152
|
+
*/
|
|
153
|
+
export async function readGlobalCursor(config) {
|
|
154
|
+
const baseDir = await getStorageDir(config);
|
|
155
|
+
const filePath = path.join(baseDir, "cursor.json");
|
|
156
|
+
try {
|
|
157
|
+
const content = await fs.readFile(filePath, "utf-8");
|
|
158
|
+
return JSON.parse(content);
|
|
159
|
+
}
|
|
160
|
+
catch {
|
|
161
|
+
return null;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* Write global cursor
|
|
166
|
+
*/
|
|
167
|
+
export async function writeGlobalCursor(cursor, config) {
|
|
168
|
+
const baseDir = await getStorageDir(config);
|
|
169
|
+
const filePath = path.join(baseDir, "cursor.json");
|
|
170
|
+
await initializeStorage(config);
|
|
171
|
+
await fs.writeFile(filePath, JSON.stringify(cursor, null, 2), "utf-8");
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* Deduplicate transactions within a time window (24 hours)
|
|
175
|
+
*/
|
|
176
|
+
export function deduplicateTransactions(transactions, windowHours = 24) {
|
|
177
|
+
const seen = new Set();
|
|
178
|
+
const windowStart = Date.now() - windowHours * 60 * 60 * 1000;
|
|
179
|
+
const result = [];
|
|
180
|
+
// Sort by date ascending
|
|
181
|
+
const sorted = [...transactions].sort((a, b) => a.date.localeCompare(b.date));
|
|
182
|
+
for (const txn of sorted) {
|
|
183
|
+
const key = `${txn.accountId}_${txn.plaidTransactionId}`;
|
|
184
|
+
const txnDate = new Date(txn.date).getTime();
|
|
185
|
+
// Only include if not seen, or outside deduplication window
|
|
186
|
+
if (!seen.has(key) || txnDate > windowStart) {
|
|
187
|
+
seen.add(key);
|
|
188
|
+
result.push(txn);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
return result;
|
|
192
|
+
}
|
|
193
|
+
//# sourceMappingURL=transaction-storage.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transaction-storage.js","sourceRoot":"","sources":["../../src/storage/transaction-storage.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/sync/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,cAAc,mBAAmB,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/sync/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,cAAc,mBAAmB,CAAA"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sync service for BillClaw - Framework-agnostic background sync logic
|
|
3
|
+
*/
|
|
4
|
+
import type { AccountConfig } from "../models/config.js";
|
|
5
|
+
import type { Logger } from "../errors/errors.js";
|
|
6
|
+
export interface SyncResult {
|
|
7
|
+
accountId: string;
|
|
8
|
+
success: boolean;
|
|
9
|
+
transactionsAdded: number;
|
|
10
|
+
transactionsUpdated: number;
|
|
11
|
+
errors?: string[];
|
|
12
|
+
}
|
|
13
|
+
export interface SyncServiceState {
|
|
14
|
+
isRunning: boolean;
|
|
15
|
+
lastSync: string | null;
|
|
16
|
+
nextSync: string | null;
|
|
17
|
+
accountsSynced: number;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Sync function interface - adapters provide the actual sync implementation
|
|
21
|
+
*/
|
|
22
|
+
export interface SyncProvider {
|
|
23
|
+
syncAccount(accountId: string): Promise<SyncResult>;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Calculate next sync time based on sync frequency
|
|
27
|
+
*/
|
|
28
|
+
export declare function calculateNextSync(frequency: string, lastSync?: Date): Date;
|
|
29
|
+
/**
|
|
30
|
+
* Check if an account is due for sync
|
|
31
|
+
*/
|
|
32
|
+
export declare function isDueForSync(account: AccountConfig): boolean;
|
|
33
|
+
/**
|
|
34
|
+
* Sync all accounts that are due for sync
|
|
35
|
+
*
|
|
36
|
+
* @param accounts - All configured accounts
|
|
37
|
+
* @param syncProvider - Provider that implements the actual sync logic
|
|
38
|
+
* @param logger - Logger for output
|
|
39
|
+
* @returns Array of sync results
|
|
40
|
+
*/
|
|
41
|
+
export declare function syncDueAccounts(accounts: AccountConfig[], syncProvider: SyncProvider, logger: Logger): Promise<SyncResult[]>;
|
|
42
|
+
//# sourceMappingURL=sync-service.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sync-service.d.ts","sourceRoot":"","sources":["../../src/sync/sync-service.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAA;AACxD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAA;AAEjD,MAAM,WAAW,UAAU;IACzB,SAAS,EAAE,MAAM,CAAA;IACjB,OAAO,EAAE,OAAO,CAAA;IAChB,iBAAiB,EAAE,MAAM,CAAA;IACzB,mBAAmB,EAAE,MAAM,CAAA;IAC3B,MAAM,CAAC,EAAE,MAAM,EAAE,CAAA;CAClB;AAED,MAAM,WAAW,gBAAgB;IAC/B,SAAS,EAAE,OAAO,CAAA;IAClB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAA;IACvB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAA;IACvB,cAAc,EAAE,MAAM,CAAA;CACvB;AAED;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B,WAAW,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC,CAAA;CACpD;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,IAAI,GAAG,IAAI,CA+B1E;AAED;;GAEG;AACH,wBAAgB,YAAY,CAAC,OAAO,EAAE,aAAa,GAAG,OAAO,CAmB5D;AAkCD;;;;;;;GAOG;AACH,wBAAsB,eAAe,CACnC,QAAQ,EAAE,aAAa,EAAE,EACzB,YAAY,EAAE,YAAY,EAC1B,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,UAAU,EAAE,CAAC,CAkCvB"}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sync service for BillClaw - Framework-agnostic background sync logic
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* Calculate next sync time based on sync frequency
|
|
6
|
+
*/
|
|
7
|
+
export function calculateNextSync(frequency, lastSync) {
|
|
8
|
+
const now = new Date();
|
|
9
|
+
const base = lastSync || now;
|
|
10
|
+
switch (frequency) {
|
|
11
|
+
case "realtime":
|
|
12
|
+
// Webhook-based, no scheduled sync
|
|
13
|
+
return new Date(0);
|
|
14
|
+
case "hourly":
|
|
15
|
+
return new Date(base.getTime() + 60 * 60 * 1000);
|
|
16
|
+
case "daily":
|
|
17
|
+
// Next day at same time
|
|
18
|
+
const nextDay = new Date(base);
|
|
19
|
+
nextDay.setDate(nextDay.getDate() + 1);
|
|
20
|
+
return nextDay;
|
|
21
|
+
case "weekly":
|
|
22
|
+
// Next week on same day
|
|
23
|
+
const nextWeek = new Date(base);
|
|
24
|
+
nextWeek.setDate(nextWeek.getDate() + 7);
|
|
25
|
+
return nextWeek;
|
|
26
|
+
case "manual":
|
|
27
|
+
// No scheduled sync
|
|
28
|
+
return new Date(0);
|
|
29
|
+
default:
|
|
30
|
+
return new Date(base.getTime() + 24 * 60 * 60 * 1000);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Check if an account is due for sync
|
|
35
|
+
*/
|
|
36
|
+
export function isDueForSync(account) {
|
|
37
|
+
if (!account.enabled || !account.lastSync) {
|
|
38
|
+
return true;
|
|
39
|
+
}
|
|
40
|
+
const lastSync = new Date(account.lastSync);
|
|
41
|
+
const nextSync = calculateNextSync(account.syncFrequency, lastSync);
|
|
42
|
+
// Manual accounts never sync automatically
|
|
43
|
+
if (account.syncFrequency === "manual") {
|
|
44
|
+
return false;
|
|
45
|
+
}
|
|
46
|
+
// Realtime accounts sync via webhook, not scheduled
|
|
47
|
+
if (account.syncFrequency === "realtime") {
|
|
48
|
+
return false;
|
|
49
|
+
}
|
|
50
|
+
return new Date() >= nextSync;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Sync a single account using the provided sync provider
|
|
54
|
+
*/
|
|
55
|
+
async function syncAccount(accountId, syncProvider, logger) {
|
|
56
|
+
try {
|
|
57
|
+
const result = await syncProvider.syncAccount(accountId);
|
|
58
|
+
if (result.success) {
|
|
59
|
+
logger.info?.(`Sync completed for ${accountId}: ${result.transactionsAdded} added, ${result.transactionsUpdated} updated`);
|
|
60
|
+
}
|
|
61
|
+
else {
|
|
62
|
+
logger.error?.(`Sync failed for ${accountId}:`, result.errors || []);
|
|
63
|
+
}
|
|
64
|
+
return result;
|
|
65
|
+
}
|
|
66
|
+
catch (error) {
|
|
67
|
+
logger.error?.(`Error syncing ${accountId}:`, error);
|
|
68
|
+
return {
|
|
69
|
+
accountId,
|
|
70
|
+
success: false,
|
|
71
|
+
transactionsAdded: 0,
|
|
72
|
+
transactionsUpdated: 0,
|
|
73
|
+
errors: [error instanceof Error ? error.message : String(error)],
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Sync all accounts that are due for sync
|
|
79
|
+
*
|
|
80
|
+
* @param accounts - All configured accounts
|
|
81
|
+
* @param syncProvider - Provider that implements the actual sync logic
|
|
82
|
+
* @param logger - Logger for output
|
|
83
|
+
* @returns Array of sync results
|
|
84
|
+
*/
|
|
85
|
+
export async function syncDueAccounts(accounts, syncProvider, logger) {
|
|
86
|
+
logger.info?.("BillClaw sync service started");
|
|
87
|
+
// Filter for enabled accounts
|
|
88
|
+
const enabledAccounts = accounts.filter((acc) => acc.enabled);
|
|
89
|
+
if (enabledAccounts.length === 0) {
|
|
90
|
+
logger.info?.("No enabled accounts to sync");
|
|
91
|
+
return [];
|
|
92
|
+
}
|
|
93
|
+
logger.info?.(`Found ${enabledAccounts.length} enabled accounts to check`);
|
|
94
|
+
const results = [];
|
|
95
|
+
let syncedCount = 0;
|
|
96
|
+
for (const account of enabledAccounts) {
|
|
97
|
+
if (isDueForSync(account)) {
|
|
98
|
+
logger.info?.(`Syncing account: ${account.name} (${account.id})`);
|
|
99
|
+
const result = await syncAccount(account.id, syncProvider, logger);
|
|
100
|
+
results.push(result);
|
|
101
|
+
if (result.success) {
|
|
102
|
+
syncedCount++;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
else {
|
|
106
|
+
logger.info?.(`Skipping ${account.name} (${account.id}): not due for sync`);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
logger.info?.(`Sync service completed: ${syncedCount} accounts synced`);
|
|
110
|
+
return results;
|
|
111
|
+
}
|
|
112
|
+
//# sourceMappingURL=sync-service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sync-service.js","sourceRoot":"","sources":["../../src/sync/sync-service.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Test fixtures for BillClaw
|
|
3
|
+
*/
|
|
4
|
+
import type { AccountConfig, BillclawConfig } from "@firela/billclaw-core";
|
|
5
|
+
/**
|
|
6
|
+
* Sample Plaid transaction data (external format)
|
|
7
|
+
*/
|
|
8
|
+
export interface PlaidTransactionData {
|
|
9
|
+
transaction_id: string;
|
|
10
|
+
account_id: string;
|
|
11
|
+
date: string;
|
|
12
|
+
amount: number;
|
|
13
|
+
iso_currency_code: string;
|
|
14
|
+
category?: string[];
|
|
15
|
+
merchant_name?: string;
|
|
16
|
+
payment_channel?: string;
|
|
17
|
+
pending?: boolean;
|
|
18
|
+
}
|
|
19
|
+
export declare const mockPlaidTransactions: PlaidTransactionData[];
|
|
20
|
+
/**
|
|
21
|
+
* Sample account configurations
|
|
22
|
+
*/
|
|
23
|
+
export declare const mockAccounts: AccountConfig[];
|
|
24
|
+
/**
|
|
25
|
+
* Sample complete configuration
|
|
26
|
+
*/
|
|
27
|
+
export declare const mockConfig: BillclawConfig;
|
|
28
|
+
/**
|
|
29
|
+
* Sample Gmail email content
|
|
30
|
+
*/
|
|
31
|
+
export declare const mockGmailEmails: {
|
|
32
|
+
id: string;
|
|
33
|
+
from: string;
|
|
34
|
+
subject: string;
|
|
35
|
+
body: string;
|
|
36
|
+
date: string;
|
|
37
|
+
}[];
|
|
38
|
+
//# sourceMappingURL=test-fixtures.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"test-fixtures.d.ts","sourceRoot":"","sources":["../src/test-fixtures.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAA;AAE1E;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACnC,cAAc,EAAE,MAAM,CAAA;IACtB,UAAU,EAAE,MAAM,CAAA;IAClB,IAAI,EAAE,MAAM,CAAA;IACZ,MAAM,EAAE,MAAM,CAAA;IACd,iBAAiB,EAAE,MAAM,CAAA;IACzB,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAA;IACnB,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,eAAe,CAAC,EAAE,MAAM,CAAA;IACxB,OAAO,CAAC,EAAE,OAAO,CAAA;CAClB;AAED,eAAO,MAAM,qBAAqB,EAAE,oBAAoB,EAuBvD,CAAA;AAED;;GAEG;AACH,eAAO,MAAM,YAAY,EAAE,aAAa,EA8BvC,CAAA;AAED;;GAEG;AACH,eAAO,MAAM,UAAU,EAAE,cAqCxB,CAAA;AAED;;GAEG;AACH,eAAO,MAAM,eAAe;;;;;;GA8B3B,CAAA"}
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Test fixtures for BillClaw
|
|
3
|
+
*/
|
|
4
|
+
export const mockPlaidTransactions = [
|
|
5
|
+
{
|
|
6
|
+
transaction_id: "plaid-txn-001",
|
|
7
|
+
account_id: "plaid-acct-123",
|
|
8
|
+
date: "2024-01-15",
|
|
9
|
+
amount: 125.5,
|
|
10
|
+
iso_currency_code: "USD",
|
|
11
|
+
category: ["Food", "Restaurants"],
|
|
12
|
+
merchant_name: "Test Restaurant",
|
|
13
|
+
payment_channel: "online",
|
|
14
|
+
pending: false,
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
transaction_id: "plaid-txn-002",
|
|
18
|
+
account_id: "plaid-acct-123",
|
|
19
|
+
date: "2024-01-16",
|
|
20
|
+
amount: 45.99,
|
|
21
|
+
iso_currency_code: "USD",
|
|
22
|
+
category: ["Shopping", "Electronics"],
|
|
23
|
+
merchant_name: "Tech Store",
|
|
24
|
+
payment_channel: "in store",
|
|
25
|
+
pending: true,
|
|
26
|
+
},
|
|
27
|
+
];
|
|
28
|
+
/**
|
|
29
|
+
* Sample account configurations
|
|
30
|
+
*/
|
|
31
|
+
export const mockAccounts = [
|
|
32
|
+
{
|
|
33
|
+
id: "plaid-sandbox-123",
|
|
34
|
+
type: "plaid",
|
|
35
|
+
name: "Test Bank Account",
|
|
36
|
+
enabled: true,
|
|
37
|
+
syncFrequency: "daily",
|
|
38
|
+
lastSync: "2024-01-16T10:30:00Z",
|
|
39
|
+
lastStatus: "success",
|
|
40
|
+
plaidItemId: "item-sandbox-123",
|
|
41
|
+
plaidAccessToken: "access-sandbox-test",
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
id: "gmail-bill-456",
|
|
45
|
+
type: "gmail",
|
|
46
|
+
name: "Gmail Bills",
|
|
47
|
+
enabled: true,
|
|
48
|
+
syncFrequency: "weekly",
|
|
49
|
+
lastSync: "2024-01-15T08:00:00Z",
|
|
50
|
+
lastStatus: "success",
|
|
51
|
+
gmailEmailAddress: "user@example.com",
|
|
52
|
+
gmailFilters: ["from:billing@*"],
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
id: "gocardless-sandbox-789",
|
|
56
|
+
type: "gocardless",
|
|
57
|
+
name: "European Bank",
|
|
58
|
+
enabled: false,
|
|
59
|
+
syncFrequency: "manual",
|
|
60
|
+
},
|
|
61
|
+
];
|
|
62
|
+
/**
|
|
63
|
+
* Sample complete configuration
|
|
64
|
+
*/
|
|
65
|
+
export const mockConfig = {
|
|
66
|
+
accounts: mockAccounts,
|
|
67
|
+
webhooks: [
|
|
68
|
+
{
|
|
69
|
+
enabled: true,
|
|
70
|
+
url: "https://example.com/webhook",
|
|
71
|
+
secret: "webhook-secret-key",
|
|
72
|
+
events: ["transaction.new", "sync.failed"],
|
|
73
|
+
retryPolicy: {
|
|
74
|
+
maxRetries: 3,
|
|
75
|
+
initialDelay: 1000,
|
|
76
|
+
maxDelay: 30000,
|
|
77
|
+
},
|
|
78
|
+
},
|
|
79
|
+
],
|
|
80
|
+
storage: {
|
|
81
|
+
path: "~/.billclaw",
|
|
82
|
+
format: "json",
|
|
83
|
+
encryption: { enabled: false },
|
|
84
|
+
},
|
|
85
|
+
sync: {
|
|
86
|
+
defaultFrequency: "daily",
|
|
87
|
+
maxRetries: 3,
|
|
88
|
+
retryOnFailure: true,
|
|
89
|
+
},
|
|
90
|
+
plaid: {
|
|
91
|
+
clientId: "test-client-id",
|
|
92
|
+
secret: "test-secret",
|
|
93
|
+
environment: "sandbox",
|
|
94
|
+
},
|
|
95
|
+
gmail: {
|
|
96
|
+
senderWhitelist: ["billing@*"],
|
|
97
|
+
keywords: ["invoice", "receipt", "statement"],
|
|
98
|
+
confidenceThreshold: 0.5,
|
|
99
|
+
requireAmount: false,
|
|
100
|
+
requireDate: false,
|
|
101
|
+
},
|
|
102
|
+
};
|
|
103
|
+
/**
|
|
104
|
+
* Sample Gmail email content
|
|
105
|
+
*/
|
|
106
|
+
export const mockGmailEmails = [
|
|
107
|
+
{
|
|
108
|
+
id: "gmail-msg-001",
|
|
109
|
+
from: "billing@company.com",
|
|
110
|
+
subject: "Your invoice for January",
|
|
111
|
+
body: `
|
|
112
|
+
Dear Customer,
|
|
113
|
+
|
|
114
|
+
Your invoice for January is ready.
|
|
115
|
+
|
|
116
|
+
Amount Due: $89.00
|
|
117
|
+
Due Date: January 31, 2024
|
|
118
|
+
|
|
119
|
+
Thank you for your business.
|
|
120
|
+
`,
|
|
121
|
+
date: "2024-01-15T10:00:00Z",
|
|
122
|
+
},
|
|
123
|
+
{
|
|
124
|
+
id: "gmail-msg-002",
|
|
125
|
+
from: "receipt@store.com",
|
|
126
|
+
subject: "Receipt for your purchase",
|
|
127
|
+
body: `
|
|
128
|
+
Thank you for your purchase!
|
|
129
|
+
|
|
130
|
+
Item: Widget Pro
|
|
131
|
+
Amount: $29.99
|
|
132
|
+
Date: 01/16/2024
|
|
133
|
+
`,
|
|
134
|
+
date: "2024-01-16T14:30:00Z",
|
|
135
|
+
},
|
|
136
|
+
];
|
|
137
|
+
//# sourceMappingURL=test-fixtures.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"test-fixtures.js","sourceRoot":"","sources":["../src/test-fixtures.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@firela/billclaw-core",
|
|
3
|
+
"version": "0.1.3",
|
|
4
|
+
"description": "BillClaw Core - Framework-agnostic business logic for financial data import",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"import": "./dist/index.js",
|
|
12
|
+
"default": "./dist/index.js"
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
"files": [
|
|
16
|
+
"dist",
|
|
17
|
+
"README.md"
|
|
18
|
+
],
|
|
19
|
+
"publishConfig": {
|
|
20
|
+
"access": "public"
|
|
21
|
+
},
|
|
22
|
+
"keywords": [
|
|
23
|
+
"financial-data",
|
|
24
|
+
"plaid",
|
|
25
|
+
"gocardless",
|
|
26
|
+
"banking",
|
|
27
|
+
"transactions",
|
|
28
|
+
"bills",
|
|
29
|
+
"data-sovereignty"
|
|
30
|
+
],
|
|
31
|
+
"author": "fire-la",
|
|
32
|
+
"license": "MIT",
|
|
33
|
+
"repository": {
|
|
34
|
+
"type": "git",
|
|
35
|
+
"url": "https://github.com/fire-la/billclaw.git",
|
|
36
|
+
"directory": "packages/core"
|
|
37
|
+
},
|
|
38
|
+
"devDependencies": {
|
|
39
|
+
"@types/node": "^25.2.0",
|
|
40
|
+
"@types/proper-lockfile": "^4.1.2",
|
|
41
|
+
"@vitest/coverage-v8": "^4.0.18",
|
|
42
|
+
"oxfmt": "^0.1.0",
|
|
43
|
+
"oxlint": "^0.15.0",
|
|
44
|
+
"typescript": "^5.8.0",
|
|
45
|
+
"vitest": "^3.0.0"
|
|
46
|
+
},
|
|
47
|
+
"optionalDependencies": {
|
|
48
|
+
"keytar": "^6.0.1"
|
|
49
|
+
},
|
|
50
|
+
"dependencies": {
|
|
51
|
+
"plaid": "^32.0.0",
|
|
52
|
+
"proper-lockfile": "^4.1.2",
|
|
53
|
+
"zod": "^3.25.0"
|
|
54
|
+
},
|
|
55
|
+
"engines": {
|
|
56
|
+
"node": ">=20.0.0"
|
|
57
|
+
},
|
|
58
|
+
"scripts": {
|
|
59
|
+
"build": "tsc",
|
|
60
|
+
"dev": "tsc --watch",
|
|
61
|
+
"test": "vitest",
|
|
62
|
+
"test:coverage": "vitest --coverage",
|
|
63
|
+
"lint": "oxlint",
|
|
64
|
+
"format": "oxfmt src/",
|
|
65
|
+
"format:write": "oxfmt -w src/",
|
|
66
|
+
"clean": "rm -rf dist"
|
|
67
|
+
}
|
|
68
|
+
}
|