@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,158 @@
1
+ /**
2
+ * File locking for BillClaw
3
+ *
4
+ * Provides inter-process file locking to prevent concurrent write conflicts.
5
+ * Uses proper-lockfile for cross-platform file locking.
6
+ *
7
+ * Use cases:
8
+ * - Preventing concurrent sync operations
9
+ * - Protecting credential writes
10
+ * - Coordinating between multiple processes
11
+ */
12
+ // Lockfile module (lazy loaded)
13
+ let lockfileModule = null;
14
+ /**
15
+ * Initialize lockfile module
16
+ */
17
+ async function initLockfile() {
18
+ if (!lockfileModule) {
19
+ try {
20
+ lockfileModule = await import("proper-lockfile");
21
+ }
22
+ catch (error) {
23
+ throw new Error("proper-lockfile is not installed. Install it with: npm install proper-lockfile");
24
+ }
25
+ }
26
+ return lockfileModule;
27
+ }
28
+ /**
29
+ * Default lock options
30
+ */
31
+ const DEFAULT_LOCK_OPTIONS = {
32
+ stale: 60 * 1000, // 1 minute
33
+ retries: {
34
+ count: 10,
35
+ min: 100,
36
+ max: 500,
37
+ },
38
+ };
39
+ /**
40
+ * File lock implementation
41
+ */
42
+ class FileLock {
43
+ filePath;
44
+ logger;
45
+ released = false;
46
+ constructor(filePath, logger) {
47
+ this.filePath = filePath;
48
+ this.logger = logger;
49
+ }
50
+ async release() {
51
+ if (this.released) {
52
+ return;
53
+ }
54
+ const lockfile = await initLockfile();
55
+ try {
56
+ await lockfile.unlock(this.filePath);
57
+ this.released = true;
58
+ this.logger?.debug?.(`Released lock: ${this.filePath}`);
59
+ }
60
+ catch (error) {
61
+ this.logger?.error?.(`Failed to release lock: ${this.filePath}`, error);
62
+ throw error;
63
+ }
64
+ }
65
+ async isLocked() {
66
+ if (this.released) {
67
+ return false;
68
+ }
69
+ const lockfile = await initLockfile();
70
+ return lockfile.check(this.filePath);
71
+ }
72
+ }
73
+ /**
74
+ * Acquire a file lock
75
+ *
76
+ * @param filePath - Path to the file to lock
77
+ * @param options - Lock options
78
+ * @returns Lock handle that must be released when done
79
+ */
80
+ export async function acquireLock(filePath, options = {}) {
81
+ const opts = { ...DEFAULT_LOCK_OPTIONS, ...options };
82
+ const lockfile = await initLockfile();
83
+ try {
84
+ await lockfile.lock(filePath, {
85
+ stale: opts.stale,
86
+ retries: opts.retries
87
+ ? {
88
+ retries: opts.retries.count,
89
+ minTimeout: opts.retries.min,
90
+ maxTimeout: opts.retries.max,
91
+ }
92
+ : undefined,
93
+ });
94
+ options.logger?.debug?.(`Acquired lock: ${filePath}`);
95
+ return new FileLock(filePath, options.logger);
96
+ }
97
+ catch (error) {
98
+ options.logger?.error?.(`Failed to acquire lock: ${filePath}`, error);
99
+ throw new Error(`Failed to acquire lock on ${filePath}: ${error instanceof Error ? error.message : String(error)}`);
100
+ }
101
+ }
102
+ /**
103
+ * Execute a function while holding a lock
104
+ *
105
+ * @param filePath - Path to the file to lock
106
+ * @param fn - Function to execute while holding the lock
107
+ * @param options - Lock options
108
+ * @returns Result of the function
109
+ */
110
+ export async function withLock(filePath, fn, options = {}) {
111
+ const lock = await acquireLock(filePath, options);
112
+ try {
113
+ return await fn();
114
+ }
115
+ finally {
116
+ await lock.release();
117
+ }
118
+ }
119
+ /**
120
+ * Check if a file is locked
121
+ *
122
+ * @param filePath - Path to the file to check
123
+ * @returns true if the file is locked
124
+ */
125
+ export async function isLocked(filePath) {
126
+ const lockfile = await initLockfile();
127
+ return lockfile.check(filePath);
128
+ }
129
+ /**
130
+ * Lock names for common operations
131
+ */
132
+ export const LockNames = {
133
+ /**
134
+ * Global sync lock (prevents concurrent sync operations)
135
+ */
136
+ SYNC: ".sync.lock",
137
+ /**
138
+ * Account-specific sync lock
139
+ */
140
+ accountSync(accountId) {
141
+ return `.sync_${accountId}.lock`;
142
+ },
143
+ /**
144
+ * Credential lock (protects credential writes)
145
+ */
146
+ CREDENTIALS: ".credentials.lock",
147
+ /**
148
+ * Account-specific credential lock
149
+ */
150
+ accountCredentials(accountId) {
151
+ return `.credentials_${accountId}.lock`;
152
+ },
153
+ /**
154
+ * Export lock (protects data export operations)
155
+ */
156
+ EXPORT: ".export.lock",
157
+ };
158
+ //# sourceMappingURL=locking.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"locking.js","sourceRoot":"","sources":["../../src/storage/locking.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,102 @@
1
+ /**
2
+ * Streaming JSON support for BillClaw
3
+ *
4
+ * Provides streaming JSON parsing and generation for large datasets.
5
+ * This keeps memory usage constant regardless of file size.
6
+ *
7
+ * Use cases:
8
+ * - Reading large transaction files without loading entire file
9
+ * - Writing large datasets incrementally
10
+ * - Processing transactions one at a time
11
+ */
12
+ import type { Transaction } from "./transaction-storage.js";
13
+ import type { Logger } from "../errors/errors.js";
14
+ /**
15
+ * Options for streaming JSON write
16
+ */
17
+ export interface StreamingWriteOptions {
18
+ batchSize?: number;
19
+ logger?: Logger;
20
+ }
21
+ /**
22
+ * Options for streaming JSON read
23
+ */
24
+ export interface StreamingReadOptions<T> {
25
+ batchSize?: number;
26
+ filter?: (item: T) => boolean;
27
+ transform?: (item: T) => T;
28
+ logger?: Logger;
29
+ }
30
+ /**
31
+ * Write items to a JSON file incrementally
32
+ *
33
+ * @param filePath - Path to output file
34
+ * @param items - Async iterable of items to write
35
+ * @param options - Write options
36
+ */
37
+ export declare function writeStreamingJson<T>(filePath: string, items: AsyncIterable<T>, options?: StreamingWriteOptions): Promise<void>;
38
+ /**
39
+ * Read items from a JSON file incrementally
40
+ *
41
+ * @param filePath - Path to input file
42
+ * @param options - Read options
43
+ * @returns Async iterable of items
44
+ */
45
+ export declare function readStreamingJson<T>(filePath: string, options?: StreamingReadOptions<T>): AsyncIterable<T>;
46
+ /**
47
+ * Stream transactions from a file
48
+ *
49
+ * @param filePath - Path to transaction file
50
+ * @param options - Stream options
51
+ * @returns Async iterable of transactions
52
+ */
53
+ export declare function streamTransactions(filePath: string, options?: StreamingReadOptions<Transaction>): AsyncIterable<Transaction>;
54
+ /**
55
+ * Write transactions to a file incrementally
56
+ *
57
+ * @param filePath - Path to output file
58
+ * @param transactions - Async iterable of transactions
59
+ * @param options - Write options
60
+ */
61
+ export declare function writeTransactionsStreaming(filePath: string, transactions: AsyncIterable<Transaction>, options?: StreamingWriteOptions): Promise<void>;
62
+ /**
63
+ * Map/reduce operations on streaming JSON
64
+ */
65
+ export declare class StreamingJsonOperations<T> {
66
+ private filePath;
67
+ private logger?;
68
+ constructor(filePath: string, logger?: Logger | undefined);
69
+ /**
70
+ * Map each item through a transformation function
71
+ */
72
+ map<U>(fn: (item: T) => U): Promise<U[]>;
73
+ /**
74
+ * Filter items
75
+ */
76
+ filter(fn: (item: T) => boolean): Promise<T[]>;
77
+ /**
78
+ * Reduce items to a single value
79
+ */
80
+ reduce<U>(fn: (acc: U, item: T) => U, initial: U): Promise<U>;
81
+ /**
82
+ * Count items matching a predicate
83
+ */
84
+ count(predicate?: (item: T) => boolean): Promise<number>;
85
+ /**
86
+ * Find the first item matching a predicate
87
+ */
88
+ find(predicate: (item: T) => boolean): Promise<T | null>;
89
+ /**
90
+ * Check if any item matches a predicate
91
+ */
92
+ some(predicate: (item: T) => boolean): Promise<boolean>;
93
+ /**
94
+ * Check if all items match a predicate
95
+ */
96
+ every(predicate: (item: T) => boolean): Promise<boolean>;
97
+ }
98
+ /**
99
+ * Create streaming operations for a file
100
+ */
101
+ export declare function createStreamingOperations<T>(filePath: string, logger?: Logger): StreamingJsonOperations<T>;
102
+ //# sourceMappingURL=streaming.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"streaming.d.ts","sourceRoot":"","sources":["../../src/storage/streaming.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,245 @@
1
+ /**
2
+ * Streaming JSON support for BillClaw
3
+ *
4
+ * Provides streaming JSON parsing and generation for large datasets.
5
+ * This keeps memory usage constant regardless of file size.
6
+ *
7
+ * Use cases:
8
+ * - Reading large transaction files without loading entire file
9
+ * - Writing large datasets incrementally
10
+ * - Processing transactions one at a time
11
+ */
12
+ import { createReadStream, createWriteStream } from "node:fs";
13
+ import { createInterface } from "node:readline";
14
+ /**
15
+ * Write items to a JSON file incrementally
16
+ *
17
+ * @param filePath - Path to output file
18
+ * @param items - Async iterable of items to write
19
+ * @param options - Write options
20
+ */
21
+ export async function writeStreamingJson(filePath, items, options = {}) {
22
+ const { batchSize = 100, logger } = options;
23
+ return new Promise((resolve, reject) => {
24
+ const writeStream = createWriteStream(filePath);
25
+ let first = true;
26
+ let count = 0;
27
+ writeStream.write("[");
28
+ (async () => {
29
+ try {
30
+ for await (const item of items) {
31
+ if (!first) {
32
+ writeStream.write(",");
33
+ }
34
+ writeStream.write(JSON.stringify(item));
35
+ first = false;
36
+ count++;
37
+ if (count % batchSize === 0) {
38
+ logger?.debug?.(`Written ${count} items to ${filePath}`);
39
+ }
40
+ }
41
+ writeStream.write("]");
42
+ writeStream.end();
43
+ logger?.info?.(`Finished writing ${count} items to ${filePath}`);
44
+ }
45
+ catch (error) {
46
+ writeStream.destroy();
47
+ reject(error);
48
+ }
49
+ })();
50
+ writeStream.on("finish", resolve);
51
+ writeStream.on("error", reject);
52
+ });
53
+ }
54
+ /**
55
+ * Read items from a JSON file incrementally
56
+ *
57
+ * @param filePath - Path to input file
58
+ * @param options - Read options
59
+ * @returns Async iterable of items
60
+ */
61
+ export async function* readStreamingJson(filePath, options = {}) {
62
+ const { filter, transform, logger } = options;
63
+ let count = 0;
64
+ const fileStream = createReadStream(filePath);
65
+ const rl = createInterface({
66
+ input: fileStream,
67
+ crlfDelay: Infinity,
68
+ });
69
+ let buffer = "";
70
+ for await (const line of rl) {
71
+ buffer += line;
72
+ // Find complete JSON objects
73
+ let braceCount = 0;
74
+ let objStart = -1;
75
+ let inString = false;
76
+ let escapeNext = false;
77
+ for (let i = 0; i < buffer.length; i++) {
78
+ const char = buffer[i];
79
+ if (escapeNext) {
80
+ escapeNext = false;
81
+ continue;
82
+ }
83
+ if (char === "\\") {
84
+ escapeNext = true;
85
+ continue;
86
+ }
87
+ if (char === '"') {
88
+ inString = !inString;
89
+ continue;
90
+ }
91
+ if (inString) {
92
+ continue;
93
+ }
94
+ if (char === "{") {
95
+ if (objStart === -1) {
96
+ objStart = i;
97
+ }
98
+ braceCount++;
99
+ }
100
+ else if (char === "}") {
101
+ braceCount--;
102
+ if (braceCount === 0 && objStart !== -1) {
103
+ const jsonStr = buffer.substring(objStart, i + 1);
104
+ buffer = buffer.substring(i + 1).trim();
105
+ try {
106
+ let item = JSON.parse(jsonStr);
107
+ if (transform) {
108
+ item = transform(item);
109
+ }
110
+ if (!filter || filter(item)) {
111
+ count++;
112
+ yield item;
113
+ }
114
+ }
115
+ catch (error) {
116
+ logger?.error?.("Failed to parse JSON item", error);
117
+ }
118
+ i = -1; // Reset position
119
+ objStart = -1;
120
+ }
121
+ }
122
+ }
123
+ }
124
+ logger?.debug?.(`Read ${count} items from ${filePath}`);
125
+ }
126
+ /**
127
+ * Stream transactions from a file
128
+ *
129
+ * @param filePath - Path to transaction file
130
+ * @param options - Stream options
131
+ * @returns Async iterable of transactions
132
+ */
133
+ export async function* streamTransactions(filePath, options) {
134
+ yield* readStreamingJson(filePath, options);
135
+ }
136
+ /**
137
+ * Write transactions to a file incrementally
138
+ *
139
+ * @param filePath - Path to output file
140
+ * @param transactions - Async iterable of transactions
141
+ * @param options - Write options
142
+ */
143
+ export async function writeTransactionsStreaming(filePath, transactions, options) {
144
+ await writeStreamingJson(filePath, transactions, options);
145
+ }
146
+ /**
147
+ * Map/reduce operations on streaming JSON
148
+ */
149
+ export class StreamingJsonOperations {
150
+ filePath;
151
+ logger;
152
+ constructor(filePath, logger) {
153
+ this.filePath = filePath;
154
+ this.logger = logger;
155
+ }
156
+ /**
157
+ * Map each item through a transformation function
158
+ */
159
+ async map(fn) {
160
+ const results = [];
161
+ for await (const item of readStreamingJson(this.filePath, {
162
+ logger: this.logger,
163
+ })) {
164
+ results.push(fn(item));
165
+ }
166
+ return results;
167
+ }
168
+ /**
169
+ * Filter items
170
+ */
171
+ async filter(fn) {
172
+ const results = [];
173
+ for await (const item of readStreamingJson(this.filePath, {
174
+ filter: fn,
175
+ logger: this.logger,
176
+ })) {
177
+ results.push(item);
178
+ }
179
+ return results;
180
+ }
181
+ /**
182
+ * Reduce items to a single value
183
+ */
184
+ async reduce(fn, initial) {
185
+ let acc = initial;
186
+ for await (const item of readStreamingJson(this.filePath, {
187
+ logger: this.logger,
188
+ })) {
189
+ acc = fn(acc, item);
190
+ }
191
+ return acc;
192
+ }
193
+ /**
194
+ * Count items matching a predicate
195
+ */
196
+ async count(predicate) {
197
+ let count = 0;
198
+ for await (const _item of readStreamingJson(this.filePath, {
199
+ filter: predicate,
200
+ logger: this.logger,
201
+ })) {
202
+ count++;
203
+ }
204
+ return count;
205
+ }
206
+ /**
207
+ * Find the first item matching a predicate
208
+ */
209
+ async find(predicate) {
210
+ for await (const item of readStreamingJson(this.filePath, {
211
+ logger: this.logger,
212
+ })) {
213
+ if (predicate(item)) {
214
+ return item;
215
+ }
216
+ }
217
+ return null;
218
+ }
219
+ /**
220
+ * Check if any item matches a predicate
221
+ */
222
+ async some(predicate) {
223
+ return (await this.find(predicate)) !== null;
224
+ }
225
+ /**
226
+ * Check if all items match a predicate
227
+ */
228
+ async every(predicate) {
229
+ for await (const item of readStreamingJson(this.filePath, {
230
+ logger: this.logger,
231
+ })) {
232
+ if (!predicate(item)) {
233
+ return false;
234
+ }
235
+ }
236
+ return true;
237
+ }
238
+ }
239
+ /**
240
+ * Create streaming operations for a file
241
+ */
242
+ export function createStreamingOperations(filePath, logger) {
243
+ return new StreamingJsonOperations(filePath, logger);
244
+ }
245
+ //# sourceMappingURL=streaming.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"streaming.js","sourceRoot":"","sources":["../../src/storage/streaming.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,101 @@
1
+ /**
2
+ * Local file storage utilities for BillClaw data
3
+ */
4
+ import type { StorageConfig } from "../models/config.js";
5
+ export interface Transaction {
6
+ transactionId: string;
7
+ accountId: string;
8
+ date: string;
9
+ amount: number;
10
+ currency: string;
11
+ category: string[];
12
+ merchantName: string;
13
+ paymentChannel: string;
14
+ pending: boolean;
15
+ plaidTransactionId: string;
16
+ createdAt: string;
17
+ }
18
+ /**
19
+ * Sync state for idempotency
20
+ */
21
+ export interface SyncState {
22
+ syncId: string;
23
+ accountId: string;
24
+ startedAt: string;
25
+ completedAt?: string;
26
+ status: "running" | "completed" | "failed";
27
+ transactionsAdded: number;
28
+ transactionsUpdated: number;
29
+ cursor: string;
30
+ error?: string;
31
+ requiresReauth?: boolean;
32
+ }
33
+ /**
34
+ * Account registry entry
35
+ */
36
+ export interface AccountRegistry {
37
+ id: string;
38
+ type: string;
39
+ name: string;
40
+ createdAt: string;
41
+ lastSync?: string;
42
+ }
43
+ /**
44
+ * Global cursor for incremental sync
45
+ */
46
+ export interface GlobalCursor {
47
+ lastSyncTime: string;
48
+ }
49
+ /**
50
+ * Get the base storage directory
51
+ */
52
+ export declare function getStorageDir(config?: StorageConfig): Promise<string>;
53
+ /**
54
+ * Initialize storage directory structure
55
+ */
56
+ export declare function initializeStorage(config?: StorageConfig): Promise<void>;
57
+ /**
58
+ * Read account registry
59
+ */
60
+ export declare function readAccountRegistry(config?: StorageConfig): Promise<AccountRegistry[]>;
61
+ /**
62
+ * Write account registry
63
+ */
64
+ export declare function writeAccountRegistry(accounts: AccountRegistry[], config?: StorageConfig): Promise<void>;
65
+ /**
66
+ * Read transactions for an account and month
67
+ */
68
+ export declare function readTransactions(accountId: string, year: number, month: number, config?: StorageConfig): Promise<Transaction[]>;
69
+ /**
70
+ * Write transactions for an account and month
71
+ * Uses atomic write (temp file + rename) for safety
72
+ */
73
+ export declare function writeTransactions(accountId: string, year: number, month: number, transactions: Transaction[], config?: StorageConfig): Promise<void>;
74
+ /**
75
+ * Append transactions to existing month file (with deduplication)
76
+ */
77
+ export declare function appendTransactions(accountId: string, year: number, month: number, newTransactions: Transaction[], config?: StorageConfig): Promise<{
78
+ added: number;
79
+ updated: number;
80
+ }>;
81
+ /**
82
+ * Read sync state for an account
83
+ */
84
+ export declare function readSyncStates(accountId: string, config?: StorageConfig): Promise<SyncState[]>;
85
+ /**
86
+ * Write sync state
87
+ */
88
+ export declare function writeSyncState(state: SyncState, config?: StorageConfig): Promise<void>;
89
+ /**
90
+ * Read global cursor
91
+ */
92
+ export declare function readGlobalCursor(config?: StorageConfig): Promise<GlobalCursor | null>;
93
+ /**
94
+ * Write global cursor
95
+ */
96
+ export declare function writeGlobalCursor(cursor: GlobalCursor, config?: StorageConfig): Promise<void>;
97
+ /**
98
+ * Deduplicate transactions within a time window (24 hours)
99
+ */
100
+ export declare function deduplicateTransactions(transactions: Transaction[], windowHours?: number): Transaction[];
101
+ //# sourceMappingURL=transaction-storage.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"transaction-storage.d.ts","sourceRoot":"","sources":["../../src/storage/transaction-storage.ts"],"names":[],"mappings":"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"}