@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.
Files changed (139) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +109 -0
  3. package/dist/billclaw.d.ts +76 -0
  4. package/dist/billclaw.d.ts.map +1 -0
  5. package/dist/billclaw.js +205 -0
  6. package/dist/billclaw.js.map +1 -0
  7. package/dist/credentials/index.d.ts +8 -0
  8. package/dist/credentials/index.d.ts.map +1 -0
  9. package/dist/credentials/index.js +8 -0
  10. package/dist/credentials/index.js.map +1 -0
  11. package/dist/credentials/keychain.d.ts +92 -0
  12. package/dist/credentials/keychain.d.ts.map +1 -0
  13. package/dist/credentials/keychain.js +172 -0
  14. package/dist/credentials/keychain.js.map +1 -0
  15. package/dist/credentials/store.d.ts +76 -0
  16. package/dist/credentials/store.d.ts.map +1 -0
  17. package/dist/credentials/store.js +144 -0
  18. package/dist/credentials/store.js.map +1 -0
  19. package/dist/errors/errors.d.ts +92 -0
  20. package/dist/errors/errors.d.ts.map +1 -0
  21. package/dist/errors/errors.js +315 -0
  22. package/dist/errors/errors.js.map +1 -0
  23. package/dist/errors/index.d.ts +7 -0
  24. package/dist/errors/index.d.ts.map +1 -0
  25. package/dist/errors/index.js +7 -0
  26. package/dist/errors/index.js.map +1 -0
  27. package/dist/exporters/beancount.d.ts +42 -0
  28. package/dist/exporters/beancount.d.ts.map +1 -0
  29. package/dist/exporters/beancount.js +141 -0
  30. package/dist/exporters/beancount.js.map +1 -0
  31. package/dist/exporters/index.d.ts +8 -0
  32. package/dist/exporters/index.d.ts.map +1 -0
  33. package/dist/exporters/index.js +8 -0
  34. package/dist/exporters/index.js.map +1 -0
  35. package/dist/exporters/ledger.d.ts +42 -0
  36. package/dist/exporters/ledger.d.ts.map +1 -0
  37. package/dist/exporters/ledger.js +139 -0
  38. package/dist/exporters/ledger.js.map +1 -0
  39. package/dist/index.d.ts +23 -0
  40. package/dist/index.d.ts.map +1 -0
  41. package/dist/index.js +34 -0
  42. package/dist/index.js.map +1 -0
  43. package/dist/models/config.d.ts +552 -0
  44. package/dist/models/config.d.ts.map +1 -0
  45. package/dist/models/config.js +168 -0
  46. package/dist/models/config.js.map +1 -0
  47. package/dist/models/index.d.ts +7 -0
  48. package/dist/models/index.d.ts.map +1 -0
  49. package/dist/models/index.js +8 -0
  50. package/dist/models/index.js.map +1 -0
  51. package/dist/runtime/index.d.ts +7 -0
  52. package/dist/runtime/index.d.ts.map +1 -0
  53. package/dist/runtime/index.js +7 -0
  54. package/dist/runtime/index.js.map +1 -0
  55. package/dist/runtime/types.d.ts +110 -0
  56. package/dist/runtime/types.d.ts.map +1 -0
  57. package/dist/runtime/types.js +85 -0
  58. package/dist/runtime/types.js.map +1 -0
  59. package/dist/security/audit.d.ts +148 -0
  60. package/dist/security/audit.d.ts.map +1 -0
  61. package/dist/security/audit.js +286 -0
  62. package/dist/security/audit.js.map +1 -0
  63. package/dist/security/index.d.ts +7 -0
  64. package/dist/security/index.d.ts.map +1 -0
  65. package/dist/security/index.js +7 -0
  66. package/dist/security/index.js.map +1 -0
  67. package/dist/services/event-emitter.d.ts +171 -0
  68. package/dist/services/event-emitter.d.ts.map +1 -0
  69. package/dist/services/event-emitter.js +287 -0
  70. package/dist/services/event-emitter.js.map +1 -0
  71. package/dist/services/index.d.ts +8 -0
  72. package/dist/services/index.d.ts.map +1 -0
  73. package/dist/services/index.js +8 -0
  74. package/dist/services/index.js.map +1 -0
  75. package/dist/sources/gmail/bill-recognizer.d.ts +71 -0
  76. package/dist/sources/gmail/bill-recognizer.d.ts.map +1 -0
  77. package/dist/sources/gmail/bill-recognizer.js +341 -0
  78. package/dist/sources/gmail/bill-recognizer.js.map +1 -0
  79. package/dist/sources/gmail/email-parser.d.ts +68 -0
  80. package/dist/sources/gmail/email-parser.d.ts.map +1 -0
  81. package/dist/sources/gmail/email-parser.js +238 -0
  82. package/dist/sources/gmail/email-parser.js.map +1 -0
  83. package/dist/sources/gmail/gmail-fetch.d.ts +54 -0
  84. package/dist/sources/gmail/gmail-fetch.d.ts.map +1 -0
  85. package/dist/sources/gmail/gmail-fetch.js +300 -0
  86. package/dist/sources/gmail/gmail-fetch.js.map +1 -0
  87. package/dist/sources/gmail/index.d.ts +7 -0
  88. package/dist/sources/gmail/index.d.ts.map +1 -0
  89. package/dist/sources/gmail/index.js +7 -0
  90. package/dist/sources/gmail/index.js.map +1 -0
  91. package/dist/sources/index.d.ts +8 -0
  92. package/dist/sources/index.d.ts.map +1 -0
  93. package/dist/sources/index.js +8 -0
  94. package/dist/sources/index.js.map +1 -0
  95. package/dist/sources/plaid/index.d.ts +7 -0
  96. package/dist/sources/plaid/index.d.ts.map +1 -0
  97. package/dist/sources/plaid/index.js +7 -0
  98. package/dist/sources/plaid/index.js.map +1 -0
  99. package/dist/sources/plaid/plaid-sync.d.ts +42 -0
  100. package/dist/sources/plaid/plaid-sync.d.ts.map +1 -0
  101. package/dist/sources/plaid/plaid-sync.js +182 -0
  102. package/dist/sources/plaid/plaid-sync.js.map +1 -0
  103. package/dist/storage/cache.d.ts +134 -0
  104. package/dist/storage/cache.d.ts.map +1 -0
  105. package/dist/storage/cache.js +239 -0
  106. package/dist/storage/cache.js.map +1 -0
  107. package/dist/storage/index.d.ts +11 -0
  108. package/dist/storage/index.d.ts.map +1 -0
  109. package/dist/storage/index.js +11 -0
  110. package/dist/storage/index.js.map +1 -0
  111. package/dist/storage/indexes.d.ts +136 -0
  112. package/dist/storage/indexes.d.ts.map +1 -0
  113. package/dist/storage/indexes.js +294 -0
  114. package/dist/storage/indexes.js.map +1 -0
  115. package/dist/storage/locking.d.ts +103 -0
  116. package/dist/storage/locking.d.ts.map +1 -0
  117. package/dist/storage/locking.js +158 -0
  118. package/dist/storage/locking.js.map +1 -0
  119. package/dist/storage/streaming.d.ts +102 -0
  120. package/dist/storage/streaming.d.ts.map +1 -0
  121. package/dist/storage/streaming.js +245 -0
  122. package/dist/storage/streaming.js.map +1 -0
  123. package/dist/storage/transaction-storage.d.ts +101 -0
  124. package/dist/storage/transaction-storage.d.ts.map +1 -0
  125. package/dist/storage/transaction-storage.js +193 -0
  126. package/dist/storage/transaction-storage.js.map +1 -0
  127. package/dist/sync/index.d.ts +7 -0
  128. package/dist/sync/index.d.ts.map +1 -0
  129. package/dist/sync/index.js +7 -0
  130. package/dist/sync/index.js.map +1 -0
  131. package/dist/sync/sync-service.d.ts +42 -0
  132. package/dist/sync/sync-service.d.ts.map +1 -0
  133. package/dist/sync/sync-service.js +112 -0
  134. package/dist/sync/sync-service.js.map +1 -0
  135. package/dist/test-fixtures.d.ts +38 -0
  136. package/dist/test-fixtures.d.ts.map +1 -0
  137. package/dist/test-fixtures.js +137 -0
  138. package/dist/test-fixtures.js.map +1 -0
  139. package/package.json +68 -0
@@ -0,0 +1,315 @@
1
+ /**
2
+ * Error handling utilities for BillClaw
3
+ *
4
+ * Provides user-friendly error messages, recovery suggestions,
5
+ * and troubleshooting guides.
6
+ *
7
+ * Framework-agnostic: Logger is provided via runtime abstraction.
8
+ */
9
+ /**
10
+ * Error categories for better user messaging
11
+ */
12
+ export var ErrorCategory;
13
+ (function (ErrorCategory) {
14
+ // Configuration errors
15
+ ErrorCategory["CONFIG"] = "configuration";
16
+ ErrorCategory["CREDENTIALS"] = "credentials";
17
+ ErrorCategory["NETWORK"] = "network";
18
+ // Plaid errors
19
+ ErrorCategory["PLAID_API"] = "plaid_api";
20
+ ErrorCategory["PLAID_AUTH"] = "plaid_auth";
21
+ ErrorCategory["PLAID_ITEM"] = "plaid_item";
22
+ // Gmail errors
23
+ ErrorCategory["GMAIL_API"] = "gmail_api";
24
+ ErrorCategory["GMAIL_AUTH"] = "gmail_auth";
25
+ // Storage errors
26
+ ErrorCategory["STORAGE"] = "storage";
27
+ ErrorCategory["FILE_SYSTEM"] = "file_system";
28
+ // General errors
29
+ ErrorCategory["UNKNOWN"] = "unknown";
30
+ })(ErrorCategory || (ErrorCategory = {}));
31
+ /**
32
+ * Create a user-friendly error
33
+ */
34
+ export function createUserError(category, title, message, suggestions, docsLink, originalError) {
35
+ return {
36
+ type: "UserError",
37
+ category,
38
+ title,
39
+ message,
40
+ suggestions,
41
+ docsLink,
42
+ error: originalError,
43
+ };
44
+ }
45
+ /**
46
+ * Format error for display to user
47
+ */
48
+ export function formatError(error) {
49
+ const lines = [];
50
+ // Header with category emoji
51
+ const categoryEmoji = getCategoryEmoji(error.category);
52
+ lines.push(`${categoryEmoji} ${error.title}`);
53
+ lines.push("");
54
+ // Message
55
+ lines.push(error.message);
56
+ lines.push("");
57
+ // Suggestions
58
+ if (error.suggestions.length > 0) {
59
+ lines.push("Suggestions:");
60
+ for (let i = 0; i < error.suggestions.length; i++) {
61
+ lines.push(` ${i + 1}. ${error.suggestions[i]}`);
62
+ }
63
+ }
64
+ // Docs link
65
+ if (error.docsLink) {
66
+ lines.push("");
67
+ lines.push(`Learn more: ${error.docsLink}`);
68
+ }
69
+ return lines.join("\n");
70
+ }
71
+ /**
72
+ * Get emoji for error category
73
+ */
74
+ function getCategoryEmoji(category) {
75
+ const emojis = {
76
+ [ErrorCategory.CONFIG]: "⚙️",
77
+ [ErrorCategory.CREDENTIALS]: "🔑",
78
+ [ErrorCategory.NETWORK]: "🌐",
79
+ [ErrorCategory.PLAID_API]: "🏦",
80
+ [ErrorCategory.PLAID_AUTH]: "🔐",
81
+ [ErrorCategory.PLAID_ITEM]: "📝",
82
+ [ErrorCategory.GMAIL_API]: "📧",
83
+ [ErrorCategory.GMAIL_AUTH]: "🔐",
84
+ [ErrorCategory.STORAGE]: "💾",
85
+ [ErrorCategory.FILE_SYSTEM]: "📁",
86
+ [ErrorCategory.UNKNOWN]: "❓",
87
+ };
88
+ return emojis[category] || "❓";
89
+ }
90
+ /**
91
+ * Parse Plaid error codes and create user-friendly errors
92
+ */
93
+ export function parsePlaidError(error) {
94
+ const errorCode = error.error_code || "UNKNOWN";
95
+ const errorMessage = error.error_message || error.display_message || "An error occurred";
96
+ const requestId = error.request_id;
97
+ // Login required
98
+ if (errorCode === "ITEM_LOGIN_REQUIRED" ||
99
+ error.error_type === "ITEM_LOGIN_REQUIRED") {
100
+ return createUserError(ErrorCategory.PLAID_AUTH, "Account Re-Authentication Required", "Your bank account requires re-authentication. This happens when your bank credentials have changed or expired.", [
101
+ "Re-authenticate via your adapter's setup command",
102
+ "This will open a secure browser window where you can log into your bank",
103
+ "After re-authentication, your transactions will sync normally",
104
+ ], "https://plaid.com/docs/errors/#item-login-required");
105
+ }
106
+ // Invalid credentials
107
+ if (errorCode === "INVALID_ACCESS_TOKEN" ||
108
+ error.error_type === "INVALID_ACCESS_TOKEN") {
109
+ return createUserError(ErrorCategory.PLAID_AUTH, "Invalid Access Token", "Your access token is invalid. This can happen if the token was revoked or corrupted.", [
110
+ "Run your adapter's setup command to reconnect your account",
111
+ "If this persists, remove and re-add the account",
112
+ ], "https://plaid.com/docs/errors/#invalid-access-token");
113
+ }
114
+ // Product not ready
115
+ if (errorCode === "PRODUCT_NOT_READY") {
116
+ return createUserError(ErrorCategory.PLAID_API, "Account Not Ready", "Your account is not fully set up yet. Plaid is still processing your account information.", [
117
+ "Wait a few minutes and try again",
118
+ "If this persists, contact Plaid support",
119
+ ], "https://plaid.com/docs/errors/#product-not-ready");
120
+ }
121
+ // Rate limit
122
+ if (errorCode === "RATE_LIMIT_EXCEEDED") {
123
+ return createUserError(ErrorCategory.PLAID_API, "API Rate Limit Exceeded", "Too many requests have been made to the Plaid API. Please wait before trying again.", [
124
+ "Wait a few minutes before syncing again",
125
+ "Consider syncing less frequently (e.g., daily instead of hourly)",
126
+ "If you need higher rate limits, upgrade your Plaid plan",
127
+ ], "https://plaid.com/docs/errors/#rate-limit-exceeded");
128
+ }
129
+ // Institution down
130
+ if (errorCode === "INSTITUTION_DOWN") {
131
+ return createUserError(ErrorCategory.PLAID_API, "Bank temporarily unavailable", "Your bank's systems are temporarily down for maintenance.", [
132
+ "Wait a few minutes and try again",
133
+ "Check your bank's website for service status updates",
134
+ "Your transactions will sync automatically once the bank is back online",
135
+ ], undefined);
136
+ }
137
+ // Invalid credentials
138
+ if (errorCode === "INVALID_CREDENTIALS") {
139
+ return createUserError(ErrorCategory.PLAID_API, "Invalid API Credentials", "The Plaid API credentials configured are invalid.", [
140
+ "Configure your Plaid client ID and secret",
141
+ "Verify your credentials at https://dashboard.plaid.com",
142
+ ], "https://dashboard.plaid.com");
143
+ }
144
+ // Generic Plaid error
145
+ return createUserError(ErrorCategory.PLAID_API, "Plaid API Error", `${errorMessage}${requestId ? ` (Request ID: ${requestId})` : ""}`, [
146
+ "Try again in a few minutes",
147
+ "Check Plaid status at https://status.plaid.com",
148
+ ], "https://plaid.com/docs/errors/");
149
+ }
150
+ /**
151
+ * Parse Gmail API errors and create user-friendly errors
152
+ */
153
+ export function parseGmailError(error) {
154
+ const statusCode = error.status || error.code || 0;
155
+ // Unauthorized
156
+ if (statusCode === 401) {
157
+ return createUserError(ErrorCategory.GMAIL_AUTH, "Gmail Authentication Failed", "Your Gmail access has expired or been revoked. You need to re-authenticate.", [
158
+ "Re-authenticate with Gmail via your adapter's setup command",
159
+ "Make sure you grant read-only access to your Gmail",
160
+ "Check that Google Cloud OAuth credentials are valid",
161
+ ], "https://developers.google.com/gmail/api/auth");
162
+ }
163
+ // Forbidden
164
+ if (statusCode === 403) {
165
+ return createUserError(ErrorCategory.GMAIL_API, "Gmail Access Denied", "Access to Gmail was denied. This usually means the OAuth token lacks the required permissions.", [
166
+ "Make sure the Gmail API is enabled in Google Cloud Console",
167
+ "Verify that the OAuth consent screen includes 'gmail.readonly' scope",
168
+ "Re-authenticate to grant proper permissions",
169
+ ], "https://developers.google.com/gmail/api/auth");
170
+ }
171
+ // Not found
172
+ if (statusCode === 404) {
173
+ return createUserError(ErrorCategory.GMAIL_API, "Gmail API Not Found", "The Gmail API endpoint could not be found. This may be a configuration issue.", [
174
+ "Verify the Gmail API is enabled in your Google Cloud project",
175
+ "Check that the API name is correct: 'gmail.api'",
176
+ "Try re-enabling the Gmail API in Google Cloud Console",
177
+ ], "https://console.cloud.google.com/apis/library/gmail-api");
178
+ }
179
+ // Rate limit
180
+ if (statusCode === 429) {
181
+ return createUserError(ErrorCategory.GMAIL_API, "Gmail API Rate Limit Exceeded", "Too many requests have been made to the Gmail API. You've hit the daily quota limit.", [
182
+ "Wait until tomorrow when the quota resets",
183
+ "Reduce sync frequency to avoid hitting the limit",
184
+ "Consider using Gmail push notifications instead of polling",
185
+ "Free tier: 250 quota units/day",
186
+ ], "https://developers.google.com/gmail/api/v1/quota");
187
+ }
188
+ // Generic Gmail error
189
+ return createUserError(ErrorCategory.GMAIL_API, "Gmail API Error", error.message || "An error occurred while communicating with Gmail", [
190
+ "Check your internet connection",
191
+ "Verify Gmail API is enabled in Google Cloud Console",
192
+ "Try again in a few minutes",
193
+ ], "https://developers.google.com/gmail/api");
194
+ }
195
+ /**
196
+ * Parse network errors
197
+ */
198
+ export function parseNetworkError(error) {
199
+ const message = error.message.toLowerCase();
200
+ // Connection refused
201
+ if (message.includes("econnrefused") ||
202
+ message.includes("connection refused")) {
203
+ return createUserError(ErrorCategory.NETWORK, "Connection Refused", "Could not connect to the server. The service may be down or your network is blocking the connection.", [
204
+ "Check your internet connection",
205
+ "Verify you're not behind a firewall or proxy",
206
+ "If using a VPN, try disconnecting it",
207
+ "Check if the service is temporarily down",
208
+ ], undefined);
209
+ }
210
+ // Timeout
211
+ if (message.includes("timeout") || message.includes("timed out")) {
212
+ return createUserError(ErrorCategory.NETWORK, "Request Timeout", "The request took too long to complete. This could be due to slow network or server issues.", [
213
+ "Check your internet connection speed",
214
+ "Try again in a few minutes",
215
+ "If syncing many transactions, consider reducing the date range",
216
+ ], undefined);
217
+ }
218
+ // DNS resolution failed
219
+ if (message.includes("enotfound") || message.includes("getaddrinfo")) {
220
+ return createUserError(ErrorCategory.NETWORK, "DNS Resolution Failed", "Could not resolve the server address. This might be a DNS or network configuration issue.", [
221
+ "Check your internet connection",
222
+ "Try switching to a different DNS server (e.g., 8.8.8.8)",
223
+ "Flush your DNS cache",
224
+ "If you're using a VPN, try disconnecting it",
225
+ ], undefined);
226
+ }
227
+ // Generic network error
228
+ return createUserError(ErrorCategory.NETWORK, "Network Error", error.message || "An error occurred while communicating with the server", [
229
+ "Check your internet connection",
230
+ "Try again in a few minutes",
231
+ "If the problem persists, check your network settings",
232
+ ], undefined);
233
+ }
234
+ /**
235
+ * Parse file system errors
236
+ */
237
+ export function parseFileSystemError(error, filePath) {
238
+ const code = error.code;
239
+ const message = error.message;
240
+ // Permission denied
241
+ if (code === "EACCES" || code === "EPERM") {
242
+ return createUserError(ErrorCategory.FILE_SYSTEM, "Permission Denied", `Cannot access ${filePath || "file or directory"}. You don't have the required permissions.`, [
243
+ "Check file/directory permissions",
244
+ "Ensure the user has read/write access to the data directory",
245
+ ], undefined);
246
+ }
247
+ // No space left
248
+ if (code === "ENOSPC") {
249
+ return createUserError(ErrorCategory.STORAGE, "Disk Full", "No space left on device. Cannot save transactions.", [
250
+ "Free up disk space by deleting unnecessary files",
251
+ "Consider moving the BillClaw data directory to a drive with more space",
252
+ ], undefined);
253
+ }
254
+ // Directory not found
255
+ if (code === "ENOENT" && message.includes("no such file")) {
256
+ return createUserError(ErrorCategory.FILE_SYSTEM, "File or Directory Not Found", `The file or directory ${filePath || ""} does not exist.`, [
257
+ "Run setup to initialize BillClaw",
258
+ "Verify the data directory path is correct",
259
+ ], undefined);
260
+ }
261
+ // Generic file system error
262
+ return createUserError(ErrorCategory.FILE_SYSTEM, "File System Error", message || "An error occurred while accessing the file system", [
263
+ "Check file/directory permissions",
264
+ "Ensure the data directory exists and is writable",
265
+ "Try running setup to reinitialize",
266
+ ], undefined);
267
+ }
268
+ /**
269
+ * Type guard to check if error is a UserError
270
+ */
271
+ export function isUserError(error) {
272
+ return (typeof error === "object" &&
273
+ error !== null &&
274
+ "type" in error &&
275
+ error.type === "UserError");
276
+ }
277
+ /**
278
+ * Log error with context for debugging
279
+ */
280
+ export function logError(logger, error, context) {
281
+ const logData = {
282
+ timestamp: new Date().toISOString(),
283
+ category: isUserError(error) ? error.category : ErrorCategory.UNKNOWN,
284
+ message: error.message,
285
+ context,
286
+ };
287
+ if (isUserError(error) && error.error) {
288
+ logData.originalError = {
289
+ name: error.error.name,
290
+ message: error.error.message,
291
+ stack: error.error.stack,
292
+ };
293
+ }
294
+ logger?.error?.("BillClaw error:", JSON.stringify(logData, null, 2));
295
+ }
296
+ /**
297
+ * Get troubleshooting guide URL for error category
298
+ */
299
+ export function getTroubleshootingUrl(category) {
300
+ const baseUrl = "https://github.com/fire-la/billclaw/blob/main/docs/troubleshooting.md";
301
+ const urls = {
302
+ [ErrorCategory.CONFIG]: `${baseUrl}#configuration-issues`,
303
+ [ErrorCategory.CREDENTIALS]: `${baseUrl}#credentials--authentication`,
304
+ [ErrorCategory.NETWORK]: `${baseUrl}#network-issues`,
305
+ [ErrorCategory.PLAID_API]: `${baseUrl}#plaid-integration`,
306
+ [ErrorCategory.PLAID_AUTH]: `${baseUrl}#credentials--authentication`,
307
+ [ErrorCategory.PLAID_ITEM]: `${baseUrl}#plaid-integration`,
308
+ [ErrorCategory.GMAIL_API]: `${baseUrl}#gmail-integration`,
309
+ [ErrorCategory.GMAIL_AUTH]: `${baseUrl}#credentials--authentication`,
310
+ [ErrorCategory.STORAGE]: `${baseUrl}#storage-issues`,
311
+ [ErrorCategory.FILE_SYSTEM]: `${baseUrl}#storage-issues`,
312
+ };
313
+ return urls[category] || baseUrl;
314
+ }
315
+ //# sourceMappingURL=errors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.js","sourceRoot":"","sources":["../../src/errors/errors.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Error handling utilities for BillClaw
3
+ *
4
+ * @packageDocumentation
5
+ */
6
+ export * from "./errors.js";
7
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/errors/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,cAAc,aAAa,CAAA"}
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Error handling utilities for BillClaw
3
+ *
4
+ * @packageDocumentation
5
+ */
6
+ export * from "./errors.js";
7
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/errors/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,cAAc,aAAa,CAAA"}
@@ -0,0 +1,42 @@
1
+ /**
2
+ * Beancount export functionality for BillClaw
3
+ *
4
+ * This module provides functionality to export BillClaw transactions
5
+ * to the Beancount plain text accounting format.
6
+ *
7
+ * Beancount format reference: https://beancount.github.io/docs/beancount_language_syntax.html
8
+ */
9
+ import type { Transaction } from "../storage/transaction-storage.js";
10
+ import type { StorageConfig } from "../models/config.js";
11
+ /**
12
+ * Beancount transaction options
13
+ */
14
+ export interface BeancountExportOptions {
15
+ accountId: string;
16
+ year: number;
17
+ month: number;
18
+ commodity?: string;
19
+ payeeAccount?: string;
20
+ tagAccounts?: Record<string, string>;
21
+ includeTags?: boolean;
22
+ dateFormat?: string;
23
+ }
24
+ /**
25
+ * Convert a BillClaw transaction to Beancount format
26
+ */
27
+ export declare function transactionToBeancount(txn: Transaction, options: BeancountExportOptions): string;
28
+ /**
29
+ * Export transactions to Beancount format
30
+ */
31
+ export declare function exportToBeancount(transactions: Transaction[], options: BeancountExportOptions): Promise<string>;
32
+ /**
33
+ * Export transactions from storage to Beancount format
34
+ */
35
+ export declare function exportStorageToBeancount(accountId: string, year: number, month: number, storageConfig: StorageConfig, options?: Partial<BeancountExportOptions>): Promise<string>;
36
+ /**
37
+ * Get recommended account mappings for Beancount
38
+ *
39
+ * Returns a suggested mapping of BillClaw categories to Beancount accounts
40
+ */
41
+ export declare function getBeancountAccountMappings(): Record<string, string>;
42
+ //# sourceMappingURL=beancount.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"beancount.d.ts","sourceRoot":"","sources":["../../src/exporters/beancount.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mCAAmC,CAAA;AACpE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAA;AAGxD;;GAEG;AACH,MAAM,WAAW,sBAAsB;IACrC,SAAS,EAAE,MAAM,CAAA;IACjB,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,EAAE,MAAM,CAAA;IACb,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IACpC,WAAW,CAAC,EAAE,OAAO,CAAA;IACrB,UAAU,CAAC,EAAE,MAAM,CAAA;CACpB;AAED;;GAEG;AACH,wBAAgB,sBAAsB,CACpC,GAAG,EAAE,WAAW,EAChB,OAAO,EAAE,sBAAsB,GAC9B,MAAM,CAiDR;AAED;;GAEG;AACH,wBAAsB,iBAAiB,CACrC,YAAY,EAAE,WAAW,EAAE,EAC3B,OAAO,EAAE,sBAAsB,GAC9B,OAAO,CAAC,MAAM,CAAC,CAoBjB;AAED;;GAEG;AACH,wBAAsB,wBAAwB,CAC5C,SAAS,EAAE,MAAM,EACjB,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,MAAM,EACb,aAAa,EAAE,aAAa,EAC5B,OAAO,CAAC,EAAE,OAAO,CAAC,sBAAsB,CAAC,GACxC,OAAO,CAAC,MAAM,CAAC,CAgBjB;AAED;;;;GAIG;AACH,wBAAgB,2BAA2B,IAAI,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAsDpE"}
@@ -0,0 +1,141 @@
1
+ /**
2
+ * Beancount export functionality for BillClaw
3
+ *
4
+ * This module provides functionality to export BillClaw transactions
5
+ * to the Beancount plain text accounting format.
6
+ *
7
+ * Beancount format reference: https://beancount.github.io/docs/beancount_language_syntax.html
8
+ */
9
+ import { readTransactions } from "../storage/transaction-storage.js";
10
+ /**
11
+ * Convert a BillClaw transaction to Beancount format
12
+ */
13
+ export function transactionToBeancount(txn, options) {
14
+ const currency = options.commodity || "USD";
15
+ // Format date
16
+ const date = txn.date;
17
+ // Build payee line
18
+ const payee = txn.merchantName || "Unknown";
19
+ const narration = txn.category?.join(", ") || "";
20
+ // Build tags
21
+ let tags = "";
22
+ if (options.includeTags) {
23
+ const sourceTag = txn.paymentChannel === "email" ? "#gmail" : "#plaid";
24
+ tags = ` ${sourceTag}`;
25
+ if (txn.pending) {
26
+ tags += " #pending";
27
+ }
28
+ }
29
+ // Build transaction header
30
+ let output = `${date} * "${payee}"${tags}\n`;
31
+ // Add narration if present
32
+ if (narration) {
33
+ output += ` "${narration}"\n`;
34
+ }
35
+ // Determine target account based on category
36
+ const targetAccount = options.tagAccounts?.[txn.category?.[0] || ""] ||
37
+ options.payeeAccount ||
38
+ "Expenses:Unknown";
39
+ // Format amount (convert from cents to dollars)
40
+ const amount = (Math.abs(txn.amount) / 100).toFixed(2);
41
+ // Build postings
42
+ if (txn.amount < 0) {
43
+ // Expense/Outflow
44
+ output += ` ${targetAccount} ${amount} ${currency}\n`;
45
+ output += ` Assets:Bank:Checking\n`;
46
+ }
47
+ else {
48
+ // Income/Inflow
49
+ output += ` Assets:Bank:Checking ${amount} ${currency}\n`;
50
+ output += ` ${targetAccount}\n`;
51
+ }
52
+ return output;
53
+ }
54
+ /**
55
+ * Export transactions to Beancount format
56
+ */
57
+ export async function exportToBeancount(transactions, options) {
58
+ // Build Beancount file header (always include this)
59
+ let output = `;; Beancount export from BillClaw\n`;
60
+ output += `;; Account: ${options.accountId}\n`;
61
+ output += `;; Period: ${options.year}-${String(options.month).padStart(2, "0")}\n`;
62
+ output += `;; Exported: ${new Date().toISOString()}\n`;
63
+ output += "\n";
64
+ if (transactions.length === 0) {
65
+ output += "; No transactions found\n";
66
+ return output;
67
+ }
68
+ // Add each transaction
69
+ for (const txn of transactions) {
70
+ output += transactionToBeancount(txn, options);
71
+ output += "\n";
72
+ }
73
+ return output;
74
+ }
75
+ /**
76
+ * Export transactions from storage to Beancount format
77
+ */
78
+ export async function exportStorageToBeancount(accountId, year, month, storageConfig, options) {
79
+ const transactions = await readTransactions(accountId, year, month, storageConfig);
80
+ const fullOptions = {
81
+ accountId,
82
+ year,
83
+ month,
84
+ ...options,
85
+ };
86
+ return exportToBeancount(transactions, fullOptions);
87
+ }
88
+ /**
89
+ * Get recommended account mappings for Beancount
90
+ *
91
+ * Returns a suggested mapping of BillClaw categories to Beancount accounts
92
+ */
93
+ export function getBeancountAccountMappings() {
94
+ return {
95
+ // Food & Dining
96
+ "Food and Drink": "Expenses:Food:Restaurants",
97
+ "Fast Food": "Expenses:Food:FastFood",
98
+ Groceries: "Expenses:Food:Groceries",
99
+ // Transportation
100
+ Travel: "Expenses:Travel",
101
+ Gasoline: "Expenses:Transport:Fuel",
102
+ Parking: "Expenses:Transport:Parking",
103
+ Taxi: "Expenses:Transport:Taxi",
104
+ "Ride Share": "Expenses:Transport:Rideshare",
105
+ "Public Transportation": "Expenses:Transport:Public",
106
+ // Shopping
107
+ Shopping: "Expenses:Shopping:General",
108
+ Electronics: "Expenses:Shopping:Electronics",
109
+ Clothing: "Expenses:Shopping:Clothing",
110
+ "Home Improvement": "Expenses:Home:Improvement",
111
+ // Entertainment
112
+ Entertainment: "Expenses:Entertainment:General",
113
+ Movies: "Expenses:Entertainment:Movies",
114
+ Music: "Expenses:Entertainment:Music",
115
+ Games: "Expenses:Entertainment:Games",
116
+ Sports: "Expenses:Entertainment:Sports",
117
+ // Bills & Utilities
118
+ Utilities: "Expenses:Utilities:General",
119
+ Electric: "Expenses:Utilities:Electric",
120
+ Gas: "Expenses:Utilities:Gas",
121
+ Water: "Expenses:Utilities:Water",
122
+ Internet: "Expenses:Utilities:Internet",
123
+ Phone: "Expenses:Utilities:Phone",
124
+ Subscription: "Expenses:Subscriptions",
125
+ // Health
126
+ Healthcare: "Expenses:Health:Medical",
127
+ Pharmacy: "Expenses:Health:Pharmacy",
128
+ Insurance: "Expenses:Insurance:Health",
129
+ // Financial
130
+ Finance: "Expenses:Financial:Fees",
131
+ "Bank Fee": "Expenses:Financial:BankFees",
132
+ Interest: "Expenses:Financial:Interest",
133
+ Transfer: "Expenses:Financial:Transfer",
134
+ // Income
135
+ Income: "Income:General",
136
+ Salary: "Income:Salary",
137
+ Refund: "Income:Refunds",
138
+ Investment: "Income:Investments",
139
+ };
140
+ }
141
+ //# sourceMappingURL=beancount.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"beancount.js","sourceRoot":"","sources":["../../src/exporters/beancount.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Data exporters for BillClaw
3
+ *
4
+ * @packageDocumentation
5
+ */
6
+ export * from "./beancount.js";
7
+ export * from "./ledger.js";
8
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/exporters/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,cAAc,gBAAgB,CAAA;AAC9B,cAAc,aAAa,CAAA"}
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Data exporters for BillClaw
3
+ *
4
+ * @packageDocumentation
5
+ */
6
+ export * from "./beancount.js";
7
+ export * from "./ledger.js";
8
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/exporters/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,cAAc,gBAAgB,CAAA;AAC9B,cAAc,aAAa,CAAA"}
@@ -0,0 +1,42 @@
1
+ /**
2
+ * Ledger (ledger-cli) export functionality for BillClaw
3
+ *
4
+ * This module provides functionality to export BillClaw transactions
5
+ * to the Ledger plain text accounting format.
6
+ *
7
+ * Ledger format reference: https://www.ledger-cli.org/3.0/doc/ledger3.html
8
+ */
9
+ import type { Transaction } from "../storage/transaction-storage.js";
10
+ import type { StorageConfig } from "../models/config.js";
11
+ /**
12
+ * Ledger transaction options
13
+ */
14
+ export interface LedgerExportOptions {
15
+ accountId: string;
16
+ year: number;
17
+ month: number;
18
+ commodity?: string;
19
+ payeeAccount?: string;
20
+ tagAccounts?: Record<string, string>;
21
+ includeTags?: boolean;
22
+ dateFormat?: string;
23
+ }
24
+ /**
25
+ * Convert a BillClaw transaction to Ledger format
26
+ */
27
+ export declare function transactionToLedger(txn: Transaction, options: LedgerExportOptions): string;
28
+ /**
29
+ * Export transactions to Ledger format
30
+ */
31
+ export declare function exportToLedger(transactions: Transaction[], options: LedgerExportOptions): Promise<string>;
32
+ /**
33
+ * Export transactions from storage to Ledger format
34
+ */
35
+ export declare function exportStorageToLedger(accountId: string, year: number, month: number, storageConfig: StorageConfig, options?: Partial<LedgerExportOptions>): Promise<string>;
36
+ /**
37
+ * Get recommended account mappings for Ledger
38
+ *
39
+ * Returns a suggested mapping of BillClaw categories to Ledger accounts
40
+ */
41
+ export declare function getLedgerAccountMappings(): Record<string, string>;
42
+ //# sourceMappingURL=ledger.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ledger.d.ts","sourceRoot":"","sources":["../../src/exporters/ledger.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mCAAmC,CAAA;AACpE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAA;AAGxD;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAClC,SAAS,EAAE,MAAM,CAAA;IACjB,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,EAAE,MAAM,CAAA;IACb,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IACpC,WAAW,CAAC,EAAE,OAAO,CAAA;IACrB,UAAU,CAAC,EAAE,MAAM,CAAA;CACpB;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CACjC,GAAG,EAAE,WAAW,EAChB,OAAO,EAAE,mBAAmB,GAC3B,MAAM,CAgDR;AAED;;GAEG;AACH,wBAAsB,cAAc,CAClC,YAAY,EAAE,WAAW,EAAE,EAC3B,OAAO,EAAE,mBAAmB,GAC3B,OAAO,CAAC,MAAM,CAAC,CAmBjB;AAED;;GAEG;AACH,wBAAsB,qBAAqB,CACzC,SAAS,EAAE,MAAM,EACjB,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,MAAM,EACb,aAAa,EAAE,aAAa,EAC5B,OAAO,CAAC,EAAE,OAAO,CAAC,mBAAmB,CAAC,GACrC,OAAO,CAAC,MAAM,CAAC,CAgBjB;AAED;;;;GAIG;AACH,wBAAgB,wBAAwB,IAAI,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAsDjE"}