@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,287 @@
1
+ /**
2
+ * Event emitter for BillClaw webhook events
3
+ *
4
+ * This module provides a centralized way to emit and forward webhook events
5
+ * to configured external webhook endpoints.
6
+ *
7
+ * Framework-agnostic: Uses Logger interface instead of framework-specific APIs.
8
+ */
9
+ import * as crypto from "node:crypto";
10
+ /**
11
+ * Emit a BillClaw event
12
+ *
13
+ * This function creates a standardized event and forwards it to all
14
+ * configured external webhooks that are subscribed to this event type.
15
+ *
16
+ * @param logger - Logger instance for logging
17
+ * @param webhooks - Array of webhook configurations
18
+ * @param eventType - Type of event to emit
19
+ * @param data - Event data
20
+ */
21
+ export async function emitEvent(logger, webhooks, eventType, data) {
22
+ const enabledWebhooks = webhooks.filter((w) => w.enabled && w.url);
23
+ if (enabledWebhooks.length === 0) {
24
+ logger.debug?.(`No webhooks configured, skipping event: ${eventType}`);
25
+ return;
26
+ }
27
+ // Create standardized event
28
+ const event = {
29
+ id: generateEventId(),
30
+ event: eventType,
31
+ timestamp: new Date().toISOString(),
32
+ version: "1.0",
33
+ data,
34
+ };
35
+ logger.info?.(`Emitting event: ${eventType} (${event.id})`);
36
+ // Forward to configured webhooks (fire-and-forget)
37
+ const promises = enabledWebhooks.map(async (webhook) => {
38
+ // Check if this webhook is subscribed to this event
39
+ if (webhook.events &&
40
+ webhook.events.length > 0 &&
41
+ !webhook.events.includes(eventType)) {
42
+ return;
43
+ }
44
+ // Skip webhooks without URL (shouldn't happen due to filter, but TypeScript needs guard)
45
+ if (!webhook.url) {
46
+ return;
47
+ }
48
+ // Send webhook with retry logic (don't await)
49
+ sendWebhook(logger, webhook, event).catch((error) => {
50
+ logger.debug?.(`Webhook emission failed:`, error);
51
+ });
52
+ });
53
+ // Fire-and-forget: don't await webhook delivery
54
+ Promise.all(promises).catch(() => {
55
+ // All promises handled individually
56
+ });
57
+ }
58
+ /**
59
+ * Emit transaction.new event
60
+ */
61
+ export async function emitTransactionNew(logger, webhooks, transaction) {
62
+ await emitEvent(logger, webhooks, "transaction.new", transaction);
63
+ }
64
+ /**
65
+ * Emit transaction.updated event
66
+ */
67
+ export async function emitTransactionUpdated(logger, webhooks, transaction) {
68
+ await emitEvent(logger, webhooks, "transaction.updated", transaction);
69
+ }
70
+ /**
71
+ * Emit transaction.deleted event
72
+ */
73
+ export async function emitTransactionDeleted(logger, webhooks, transactionId, accountId) {
74
+ await emitEvent(logger, webhooks, "transaction.deleted", {
75
+ transactionId,
76
+ accountId,
77
+ });
78
+ }
79
+ /**
80
+ * Emit sync.started event
81
+ */
82
+ export async function emitSyncStarted(logger, webhooks, accountId, syncId) {
83
+ await emitEvent(logger, webhooks, "sync.started", {
84
+ accountId,
85
+ syncId,
86
+ status: "started",
87
+ });
88
+ }
89
+ /**
90
+ * Emit sync.completed event
91
+ */
92
+ export async function emitSyncCompleted(logger, webhooks, syncData) {
93
+ await emitEvent(logger, webhooks, "sync.completed", syncData);
94
+ }
95
+ /**
96
+ * Emit sync.failed event
97
+ */
98
+ export async function emitSyncFailed(logger, webhooks, accountId, syncId, error) {
99
+ await emitEvent(logger, webhooks, "sync.failed", {
100
+ accountId,
101
+ syncId,
102
+ status: "failed",
103
+ error,
104
+ });
105
+ }
106
+ /**
107
+ * Emit account.connected event
108
+ */
109
+ export async function emitAccountConnected(logger, webhooks, accountId, accountType) {
110
+ await emitEvent(logger, webhooks, "account.connected", {
111
+ accountId,
112
+ accountType,
113
+ status: "connected",
114
+ });
115
+ }
116
+ /**
117
+ * Emit account.disconnected event
118
+ */
119
+ export async function emitAccountDisconnected(logger, webhooks, accountId, accountType) {
120
+ await emitEvent(logger, webhooks, "account.disconnected", {
121
+ accountId,
122
+ accountType,
123
+ status: "disconnected",
124
+ });
125
+ }
126
+ /**
127
+ * Emit account.error event
128
+ */
129
+ export async function emitAccountError(logger, webhooks, accountId, accountType, error) {
130
+ await emitEvent(logger, webhooks, "account.error", {
131
+ accountId,
132
+ accountType,
133
+ status: "error",
134
+ error,
135
+ });
136
+ }
137
+ /**
138
+ * Emit webhook.test event
139
+ */
140
+ export async function emitWebhookTest(logger, webhooks, message = "Test webhook from BillClaw") {
141
+ await emitEvent(logger, webhooks, "webhook.test", {
142
+ message,
143
+ triggeredBy: "user",
144
+ });
145
+ }
146
+ /**
147
+ * Send webhook to external endpoint with signature and retry logic
148
+ *
149
+ * @param logger - Logger instance
150
+ * @param webhook - Webhook configuration with url, secret, retryPolicy
151
+ * @param event - Event to send
152
+ */
153
+ async function sendWebhook(logger, webhook, event) {
154
+ const maxRetries = webhook.retryPolicy?.maxRetries || 3;
155
+ const initialDelay = webhook.retryPolicy?.initialDelay || 1000;
156
+ const maxDelay = webhook.retryPolicy?.maxDelay || 30000;
157
+ // Create payload
158
+ const payload = { ...event };
159
+ // Sign payload if secret is configured
160
+ if (webhook.secret) {
161
+ payload.signature = generateSignature(event, webhook.secret);
162
+ }
163
+ let attempt = 0;
164
+ let delay = initialDelay;
165
+ while (attempt < maxRetries) {
166
+ try {
167
+ const response = await fetch(webhook.url, {
168
+ method: "POST",
169
+ headers: {
170
+ "Content-Type": "application/json",
171
+ "User-Agent": "BillClaw/1.0",
172
+ "X-Billclaw-Event-Id": event.id,
173
+ "X-Billclaw-Event-Type": event.event,
174
+ "X-Billclaw-Timestamp": event.timestamp,
175
+ ...(payload.signature && {
176
+ "X-Billclaw-Signature": payload.signature,
177
+ }),
178
+ },
179
+ body: JSON.stringify(payload),
180
+ });
181
+ if (response.ok) {
182
+ logger.info?.(`Webhook sent successfully to ${webhook.url}`);
183
+ return;
184
+ }
185
+ // Don't retry client errors (4xx)
186
+ if (response.status >= 400 && response.status < 500) {
187
+ logger.warn?.(`Webhook rejected by ${webhook.url}: ${response.status}`);
188
+ return;
189
+ }
190
+ throw new Error(`Webhook failed: ${response.status}`);
191
+ }
192
+ catch (error) {
193
+ attempt++;
194
+ if (attempt >= maxRetries) {
195
+ logger.error?.(`Webhook failed after ${maxRetries} retries to ${webhook.url}:`, error);
196
+ return;
197
+ }
198
+ // Exponential backoff with jitter (30%)
199
+ const jitter = Math.random() * 0.3 * delay;
200
+ await new Promise((resolve) => setTimeout(resolve, delay + jitter));
201
+ delay = Math.min(delay * 2, maxDelay);
202
+ }
203
+ }
204
+ }
205
+ /**
206
+ * Generate HMAC-SHA256 signature for webhook payload
207
+ *
208
+ * @param event - Event to sign
209
+ * @param secret - Secret key for HMAC
210
+ * @returns Signature in format "sha256=<hex>"
211
+ */
212
+ export function generateSignature(event, secret) {
213
+ // Create payload to sign (all fields except signature)
214
+ const payload = {
215
+ id: event.id,
216
+ event: event.event,
217
+ timestamp: event.timestamp,
218
+ version: event.version,
219
+ data: event.data,
220
+ };
221
+ const payloadString = JSON.stringify(payload);
222
+ // Compute HMAC-SHA256
223
+ const hmac = crypto.createHmac("sha256", secret);
224
+ hmac.update(payloadString);
225
+ return `sha256=${hmac.digest("hex")}`;
226
+ }
227
+ /**
228
+ * Verify webhook signature
229
+ *
230
+ * Returns true if the signature matches the computed signature.
231
+ * Uses timing-safe comparison to prevent timing attacks.
232
+ *
233
+ * @param payload - JSON string payload
234
+ * @param signature - Signature to verify (format: "sha256=<hex>")
235
+ * @param secret - Secret key for HMAC
236
+ * @returns True if signature is valid
237
+ */
238
+ export function verifySignature(payload, signature, secret) {
239
+ try {
240
+ const expectedSignature = crypto
241
+ .createHmac("sha256", secret)
242
+ .update(payload)
243
+ .digest("hex");
244
+ const providedSignature = signature.replace("sha256=", "");
245
+ // Use timing-safe comparison to prevent timing attacks
246
+ return crypto.timingSafeEqual(Buffer.from(expectedSignature), Buffer.from(providedSignature));
247
+ }
248
+ catch {
249
+ return false;
250
+ }
251
+ }
252
+ /**
253
+ * Generate unique event ID
254
+ *
255
+ * Format: evt_<timestamp>_<random>
256
+ * Example: evt_lz1h2x3a4b5c6d_abc123def4567
257
+ */
258
+ function generateEventId() {
259
+ const timestamp = Date.now().toString(36);
260
+ const random = Math.random().toString(36).substring(2, 15);
261
+ return `evt_${timestamp}_${random}`;
262
+ }
263
+ /**
264
+ * Type guard to check if an event is a transaction event
265
+ */
266
+ export function isTransactionEvent(event) {
267
+ return (event.event === "transaction.new" ||
268
+ event.event === "transaction.updated" ||
269
+ event.event === "transaction.deleted");
270
+ }
271
+ /**
272
+ * Type guard to check if an event is a sync event
273
+ */
274
+ export function isSyncEvent(event) {
275
+ return (event.event === "sync.started" ||
276
+ event.event === "sync.completed" ||
277
+ event.event === "sync.failed");
278
+ }
279
+ /**
280
+ * Type guard to check if an event is an account event
281
+ */
282
+ export function isAccountEvent(event) {
283
+ return (event.event === "account.connected" ||
284
+ event.event === "account.disconnected" ||
285
+ event.event === "account.error");
286
+ }
287
+ //# sourceMappingURL=event-emitter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"event-emitter.js","sourceRoot":"","sources":["../../src/services/event-emitter.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAIH,OAAO,KAAK,MAAM,MAAM,aAAa,CAAA;AA+ErC;;;;;;;;;;GAUG;AACH,MAAM,CAAC,KAAK,UAAU,SAAS,CAC7B,MAAc,EACd,QAAyB,EACzB,SAA2B,EAC3B,IAAa;IAEb,MAAM,eAAe,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,IAAI,CAAC,CAAC,GAAG,CAAC,CAAA;IAElE,IAAI,eAAe,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACjC,MAAM,CAAC,KAAK,EAAE,CAAC,2CAA2C,SAAS,EAAE,CAAC,CAAA;QACtE,OAAM;IACR,CAAC;IAED,4BAA4B;IAC5B,MAAM,KAAK,GAAkB;QAC3B,EAAE,EAAE,eAAe,EAAE;QACrB,KAAK,EAAE,SAAS;QAChB,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;QACnC,OAAO,EAAE,KAAK;QACd,IAAI;KACL,CAAA;IAED,MAAM,CAAC,IAAI,EAAE,CAAC,mBAAmB,SAAS,KAAK,KAAK,CAAC,EAAE,GAAG,CAAC,CAAA;IAE3D,mDAAmD;IACnD,MAAM,QAAQ,GAAG,eAAe,CAAC,GAAG,CAAC,KAAK,EAAE,OAAO,EAAE,EAAE;QACrD,oDAAoD;QACpD,IACE,OAAO,CAAC,MAAM;YACd,OAAO,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC;YACzB,CAAC,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,SAAS,CAAC,EACnC,CAAC;YACD,OAAM;QACR,CAAC;QAED,yFAAyF;QACzF,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC;YACjB,OAAM;QACR,CAAC;QAED,8CAA8C;QAC9C,WAAW,CACT,MAAM,EACN,OAAsE,EACtE,KAAK,CACN,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE;YAChB,MAAM,CAAC,KAAK,EAAE,CAAC,0BAA0B,EAAE,KAAK,CAAC,CAAA;QACnD,CAAC,CAAC,CAAA;IACJ,CAAC,CAAC,CAAA;IAEF,gDAAgD;IAChD,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE;QAC/B,oCAAoC;IACtC,CAAC,CAAC,CAAA;AACJ,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,kBAAkB,CACtC,MAAc,EACd,QAAyB,EACzB,WAAiC;IAEjC,MAAM,SAAS,CAAC,MAAM,EAAE,QAAQ,EAAE,iBAAiB,EAAE,WAAW,CAAC,CAAA;AACnE,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,sBAAsB,CAC1C,MAAc,EACd,QAAyB,EACzB,WAAiC;IAEjC,MAAM,SAAS,CAAC,MAAM,EAAE,QAAQ,EAAE,qBAAqB,EAAE,WAAW,CAAC,CAAA;AACvE,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,sBAAsB,CAC1C,MAAc,EACd,QAAyB,EACzB,aAAqB,EACrB,SAAiB;IAEjB,MAAM,SAAS,CAAC,MAAM,EAAE,QAAQ,EAAE,qBAAqB,EAAE;QACvD,aAAa;QACb,SAAS;KACV,CAAC,CAAA;AACJ,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,eAAe,CACnC,MAAc,EACd,QAAyB,EACzB,SAAiB,EACjB,MAAc;IAEd,MAAM,SAAS,CAAC,MAAM,EAAE,QAAQ,EAAE,cAAc,EAAE;QAChD,SAAS;QACT,MAAM;QACN,MAAM,EAAE,SAAkB;KAC3B,CAAC,CAAA;AACJ,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,iBAAiB,CACrC,MAAc,EACd,QAAyB,EACzB,QAAuB;IAEvB,MAAM,SAAS,CAAC,MAAM,EAAE,QAAQ,EAAE,gBAAgB,EAAE,QAAQ,CAAC,CAAA;AAC/D,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,cAAc,CAClC,MAAc,EACd,QAAyB,EACzB,SAAiB,EACjB,MAAc,EACd,KAAa;IAEb,MAAM,SAAS,CAAC,MAAM,EAAE,QAAQ,EAAE,aAAa,EAAE;QAC/C,SAAS;QACT,MAAM;QACN,MAAM,EAAE,QAAiB;QACzB,KAAK;KACN,CAAC,CAAA;AACJ,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,oBAAoB,CACxC,MAAc,EACd,QAAyB,EACzB,SAAiB,EACjB,WAA6C;IAE7C,MAAM,SAAS,CAAC,MAAM,EAAE,QAAQ,EAAE,mBAAmB,EAAE;QACrD,SAAS;QACT,WAAW;QACX,MAAM,EAAE,WAAoB;KAC7B,CAAC,CAAA;AACJ,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,uBAAuB,CAC3C,MAAc,EACd,QAAyB,EACzB,SAAiB,EACjB,WAA6C;IAE7C,MAAM,SAAS,CAAC,MAAM,EAAE,QAAQ,EAAE,sBAAsB,EAAE;QACxD,SAAS;QACT,WAAW;QACX,MAAM,EAAE,cAAuB;KAChC,CAAC,CAAA;AACJ,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,gBAAgB,CACpC,MAAc,EACd,QAAyB,EACzB,SAAiB,EACjB,WAA6C,EAC7C,KAAa;IAEb,MAAM,SAAS,CAAC,MAAM,EAAE,QAAQ,EAAE,eAAe,EAAE;QACjD,SAAS;QACT,WAAW;QACX,MAAM,EAAE,OAAgB;QACxB,KAAK;KACN,CAAC,CAAA;AACJ,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,eAAe,CACnC,MAAc,EACd,QAAyB,EACzB,UAAkB,4BAA4B;IAE9C,MAAM,SAAS,CAAC,MAAM,EAAE,QAAQ,EAAE,cAAc,EAAE;QAChD,OAAO;QACP,WAAW,EAAE,MAAM;KACpB,CAAC,CAAA;AACJ,CAAC;AAED;;;;;;GAMG;AACH,KAAK,UAAU,WAAW,CACxB,MAAc,EACd,OAAoE,EACpE,KAAoB;IAEpB,MAAM,UAAU,GAAG,OAAO,CAAC,WAAW,EAAE,UAAU,IAAI,CAAC,CAAA;IACvD,MAAM,YAAY,GAAG,OAAO,CAAC,WAAW,EAAE,YAAY,IAAI,IAAI,CAAA;IAC9D,MAAM,QAAQ,GAAG,OAAO,CAAC,WAAW,EAAE,QAAQ,IAAI,KAAK,CAAA;IAEvD,iBAAiB;IACjB,MAAM,OAAO,GAAmB,EAAE,GAAG,KAAK,EAAE,CAAA;IAE5C,uCAAuC;IACvC,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;QACnB,OAAO,CAAC,SAAS,GAAG,iBAAiB,CAAC,KAAK,EAAE,OAAO,CAAC,MAAM,CAAC,CAAA;IAC9D,CAAC;IAED,IAAI,OAAO,GAAG,CAAC,CAAA;IACf,IAAI,KAAK,GAAG,YAAY,CAAA;IAExB,OAAO,OAAO,GAAG,UAAU,EAAE,CAAC;QAC5B,IAAI,CAAC;YACH,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,OAAO,CAAC,GAAG,EAAE;gBACxC,MAAM,EAAE,MAAM;gBACd,OAAO,EAAE;oBACP,cAAc,EAAE,kBAAkB;oBAClC,YAAY,EAAE,cAAc;oBAC5B,qBAAqB,EAAE,KAAK,CAAC,EAAE;oBAC/B,uBAAuB,EAAE,KAAK,CAAC,KAAK;oBACpC,sBAAsB,EAAE,KAAK,CAAC,SAAS;oBACvC,GAAG,CAAC,OAAO,CAAC,SAAS,IAAI;wBACvB,sBAAsB,EAAE,OAAO,CAAC,SAAS;qBAC1C,CAAC;iBACH;gBACD,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC;aAC9B,CAAC,CAAA;YAEF,IAAI,QAAQ,CAAC,EAAE,EAAE,CAAC;gBAChB,MAAM,CAAC,IAAI,EAAE,CAAC,gCAAgC,OAAO,CAAC,GAAG,EAAE,CAAC,CAAA;gBAC5D,OAAM;YACR,CAAC;YAED,kCAAkC;YAClC,IAAI,QAAQ,CAAC,MAAM,IAAI,GAAG,IAAI,QAAQ,CAAC,MAAM,GAAG,GAAG,EAAE,CAAC;gBACpD,MAAM,CAAC,IAAI,EAAE,CAAC,uBAAuB,OAAO,CAAC,GAAG,KAAK,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAA;gBACvE,OAAM;YACR,CAAC;YAED,MAAM,IAAI,KAAK,CAAC,mBAAmB,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAA;QACvD,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,EAAE,CAAA;YACT,IAAI,OAAO,IAAI,UAAU,EAAE,CAAC;gBAC1B,MAAM,CAAC,KAAK,EAAE,CACZ,wBAAwB,UAAU,eAAe,OAAO,CAAC,GAAG,GAAG,EAC/D,KAAK,CACN,CAAA;gBACD,OAAM;YACR,CAAC;YAED,wCAAwC;YACxC,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,EAAE,GAAG,GAAG,GAAG,KAAK,CAAA;YAC1C,MAAM,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,KAAK,GAAG,MAAM,CAAC,CAAC,CAAA;YACnE,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,KAAK,GAAG,CAAC,EAAE,QAAQ,CAAC,CAAA;QACvC,CAAC;IACH,CAAC;AACH,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,iBAAiB,CAC/B,KAAoB,EACpB,MAAc;IAEd,uDAAuD;IACvD,MAAM,OAAO,GAAG;QACd,EAAE,EAAE,KAAK,CAAC,EAAE;QACZ,KAAK,EAAE,KAAK,CAAC,KAAK;QAClB,SAAS,EAAE,KAAK,CAAC,SAAS;QAC1B,OAAO,EAAE,KAAK,CAAC,OAAO;QACtB,IAAI,EAAE,KAAK,CAAC,IAAI;KACjB,CAAA;IAED,MAAM,aAAa,GAAG,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,CAAA;IAE7C,sBAAsB;IACtB,MAAM,IAAI,GAAG,MAAM,CAAC,UAAU,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAA;IAChD,IAAI,CAAC,MAAM,CAAC,aAAa,CAAC,CAAA;IAC1B,OAAO,UAAU,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAA;AACvC,CAAC;AAED;;;;;;;;;;GAUG;AACH,MAAM,UAAU,eAAe,CAC7B,OAAe,EACf,SAAiB,EACjB,MAAc;IAEd,IAAI,CAAC;QACH,MAAM,iBAAiB,GAAG,MAAM;aAC7B,UAAU,CAAC,QAAQ,EAAE,MAAM,CAAC;aAC5B,MAAM,CAAC,OAAO,CAAC;aACf,MAAM,CAAC,KAAK,CAAC,CAAA;QAEhB,MAAM,iBAAiB,GAAG,SAAS,CAAC,OAAO,CAAC,SAAS,EAAE,EAAE,CAAC,CAAA;QAE1D,uDAAuD;QACvD,OAAO,MAAM,CAAC,eAAe,CAC3B,MAAM,CAAC,IAAI,CAAC,iBAAiB,CAAC,EAC9B,MAAM,CAAC,IAAI,CAAC,iBAAiB,CAAC,CAC/B,CAAA;IACH,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAA;IACd,CAAC;AACH,CAAC;AAED;;;;;GAKG;AACH,SAAS,eAAe;IACtB,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAA;IACzC,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,SAAS,CAAC,CAAC,EAAE,EAAE,CAAC,CAAA;IAC1D,OAAO,OAAO,SAAS,IAAI,MAAM,EAAE,CAAA;AACrC,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,kBAAkB,CAChC,KAAoB;IAEpB,OAAO,CACL,KAAK,CAAC,KAAK,KAAK,iBAAiB;QACjC,KAAK,CAAC,KAAK,KAAK,qBAAqB;QACrC,KAAK,CAAC,KAAK,KAAK,qBAAqB,CACtC,CAAA;AACH,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,WAAW,CACzB,KAAoB;IAEpB,OAAO,CACL,KAAK,CAAC,KAAK,KAAK,cAAc;QAC9B,KAAK,CAAC,KAAK,KAAK,gBAAgB;QAChC,KAAK,CAAC,KAAK,KAAK,aAAa,CAC9B,CAAA;AACH,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,cAAc,CAC5B,KAAoB;IAEpB,OAAO,CACL,KAAK,CAAC,KAAK,KAAK,mBAAmB;QACnC,KAAK,CAAC,KAAK,KAAK,sBAAsB;QACtC,KAAK,CAAC,KAAK,KAAK,eAAe,CAChC,CAAA;AACH,CAAC"}
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Services module for BillClaw core package
3
+ *
4
+ * This module exports service utilities for webhook events,
5
+ * including event emission, signature generation, and retry logic.
6
+ */
7
+ export * from "./event-emitter.js";
8
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/services/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,cAAc,oBAAoB,CAAA"}
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Services module for BillClaw core package
3
+ *
4
+ * This module exports service utilities for webhook events,
5
+ * including event emission, signature generation, and retry logic.
6
+ */
7
+ export * from "./event-emitter.js";
8
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/services/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,cAAc,oBAAoB,CAAA"}
@@ -0,0 +1,71 @@
1
+ /**
2
+ * Bill Recognition Service - Framework-agnostic bill detection from email content
3
+ *
4
+ * Analyzes email content to determine if it's a bill and extracts relevant information.
5
+ * Uses keyword matching, sender detection, and confidence scoring.
6
+ */
7
+ export interface EmailContent {
8
+ id: string;
9
+ subject: string;
10
+ from: string;
11
+ to?: string;
12
+ date: string;
13
+ body: string;
14
+ attachments?: Array<{
15
+ filename: string;
16
+ contentType: string;
17
+ data?: string;
18
+ }>;
19
+ }
20
+ export interface GmailConfig {
21
+ clientId?: string;
22
+ clientSecret?: string;
23
+ historyId?: string;
24
+ pubsubTopic?: string;
25
+ senderWhitelist: string[];
26
+ keywords: string[];
27
+ confidenceThreshold: number;
28
+ requireAmount: boolean;
29
+ requireDate: boolean;
30
+ billTypePatterns?: Record<string, string[]>;
31
+ }
32
+ export interface BillRecognition {
33
+ isBill: boolean;
34
+ confidence: number;
35
+ amount?: number;
36
+ currency?: string;
37
+ dueDate?: string;
38
+ billType?: string;
39
+ merchant?: string;
40
+ accountNumber?: string;
41
+ reasons: string[];
42
+ }
43
+ /**
44
+ * Default bill detection keywords
45
+ * Includes both phrases and individual words for better matching
46
+ */
47
+ export declare const DEFAULT_KEYWORDS: string[];
48
+ /**
49
+ * Known billing domains (extracted from common service providers)
50
+ */
51
+ export declare const KNOWN_BILLING_DOMAINS: string[];
52
+ /**
53
+ * Extract domain from email address
54
+ */
55
+ export declare function extractDomain(email: string): string;
56
+ /**
57
+ * Recognize if an email is a bill and extract relevant information
58
+ */
59
+ export declare function recognizeBill(email: EmailContent, config: GmailConfig): BillRecognition;
60
+ /**
61
+ * Recognize bills from multiple emails
62
+ */
63
+ export declare function recognizeBills(emails: EmailContent[], config: GmailConfig): BillRecognition[];
64
+ /**
65
+ * Filter emails to only bills based on recognition
66
+ */
67
+ export declare function filterToBills(emails: EmailContent[], config: GmailConfig): Array<{
68
+ email: EmailContent;
69
+ recognition: BillRecognition;
70
+ }>;
71
+ //# sourceMappingURL=bill-recognizer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bill-recognizer.d.ts","sourceRoot":"","sources":["../../../src/sources/gmail/bill-recognizer.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,MAAM,CAAA;IACV,OAAO,EAAE,MAAM,CAAA;IACf,IAAI,EAAE,MAAM,CAAA;IACZ,EAAE,CAAC,EAAE,MAAM,CAAA;IACX,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,MAAM,CAAA;IACZ,WAAW,CAAC,EAAE,KAAK,CAAC;QAClB,QAAQ,EAAE,MAAM,CAAA;QAChB,WAAW,EAAE,MAAM,CAAA;QACnB,IAAI,CAAC,EAAE,MAAM,CAAA;KACd,CAAC,CAAA;CACH;AAED,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,eAAe,EAAE,MAAM,EAAE,CAAA;IACzB,QAAQ,EAAE,MAAM,EAAE,CAAA;IAClB,mBAAmB,EAAE,MAAM,CAAA;IAC3B,aAAa,EAAE,OAAO,CAAA;IACtB,WAAW,EAAE,OAAO,CAAA;IACpB,gBAAgB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAA;CAC5C;AAED,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,OAAO,CAAA;IACf,UAAU,EAAE,MAAM,CAAA;IAClB,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,OAAO,EAAE,MAAM,EAAE,CAAA;CAClB;AAED;;;GAGG;AACH,eAAO,MAAM,gBAAgB,UAgC5B,CAAA;AAED;;GAEG;AACH,eAAO,MAAM,qBAAqB,UAmBjC,CAAA;AAyCD;;GAEG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAGnD;AAkKD;;GAEG;AACH,wBAAgB,aAAa,CAC3B,KAAK,EAAE,YAAY,EACnB,MAAM,EAAE,WAAW,GAClB,eAAe,CAkGjB;AAED;;GAEG;AACH,wBAAgB,cAAc,CAC5B,MAAM,EAAE,YAAY,EAAE,EACtB,MAAM,EAAE,WAAW,GAClB,eAAe,EAAE,CAEnB;AAED;;GAEG;AACH,wBAAgB,aAAa,CAC3B,MAAM,EAAE,YAAY,EAAE,EACtB,MAAM,EAAE,WAAW,GAClB,KAAK,CAAC;IAAE,KAAK,EAAE,YAAY,CAAC;IAAC,WAAW,EAAE,eAAe,CAAA;CAAE,CAAC,CAO9D"}