@nathanvale/chatline 0.0.1 → 0.0.2-next.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (213) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/dist/bin/index.js +1 -1
  3. package/dist/index.js +1 -1
  4. package/package.json +1 -1
  5. package/dist/cli/commands/clean.d.ts +0 -17
  6. package/dist/cli/commands/clean.d.ts.map +0 -1
  7. package/dist/cli/commands/clean.js +0 -142
  8. package/dist/cli/commands/clean.js.map +0 -1
  9. package/dist/cli/commands/doctor.d.ts +0 -17
  10. package/dist/cli/commands/doctor.d.ts.map +0 -1
  11. package/dist/cli/commands/doctor.js +0 -202
  12. package/dist/cli/commands/doctor.js.map +0 -1
  13. package/dist/cli/commands/enrich-ai.d.ts +0 -17
  14. package/dist/cli/commands/enrich-ai.d.ts.map +0 -1
  15. package/dist/cli/commands/enrich-ai.js +0 -371
  16. package/dist/cli/commands/enrich-ai.js.map +0 -1
  17. package/dist/cli/commands/index.d.ts +0 -16
  18. package/dist/cli/commands/index.d.ts.map +0 -1
  19. package/dist/cli/commands/index.js +0 -16
  20. package/dist/cli/commands/index.js.map +0 -1
  21. package/dist/cli/commands/ingest-csv.d.ts +0 -17
  22. package/dist/cli/commands/ingest-csv.d.ts.map +0 -1
  23. package/dist/cli/commands/ingest-csv.js +0 -138
  24. package/dist/cli/commands/ingest-csv.js.map +0 -1
  25. package/dist/cli/commands/ingest-db.d.ts +0 -17
  26. package/dist/cli/commands/ingest-db.d.ts.map +0 -1
  27. package/dist/cli/commands/ingest-db.js +0 -159
  28. package/dist/cli/commands/ingest-db.js.map +0 -1
  29. package/dist/cli/commands/init.d.ts +0 -17
  30. package/dist/cli/commands/init.d.ts.map +0 -1
  31. package/dist/cli/commands/init.js +0 -110
  32. package/dist/cli/commands/init.js.map +0 -1
  33. package/dist/cli/commands/normalize-link.d.ts +0 -16
  34. package/dist/cli/commands/normalize-link.d.ts.map +0 -1
  35. package/dist/cli/commands/normalize-link.js +0 -144
  36. package/dist/cli/commands/normalize-link.js.map +0 -1
  37. package/dist/cli/commands/render-markdown.d.ts +0 -17
  38. package/dist/cli/commands/render-markdown.d.ts.map +0 -1
  39. package/dist/cli/commands/render-markdown.js +0 -218
  40. package/dist/cli/commands/render-markdown.js.map +0 -1
  41. package/dist/cli/commands/stats.d.ts +0 -17
  42. package/dist/cli/commands/stats.d.ts.map +0 -1
  43. package/dist/cli/commands/stats.js +0 -175
  44. package/dist/cli/commands/stats.js.map +0 -1
  45. package/dist/cli/commands/validate.d.ts +0 -17
  46. package/dist/cli/commands/validate.d.ts.map +0 -1
  47. package/dist/cli/commands/validate.js +0 -152
  48. package/dist/cli/commands/validate.js.map +0 -1
  49. package/dist/cli/index.d.ts +0 -13
  50. package/dist/cli/index.d.ts.map +0 -1
  51. package/dist/cli/index.js +0 -121
  52. package/dist/cli/index.js.map +0 -1
  53. package/dist/cli/types.d.ts +0 -93
  54. package/dist/cli/types.d.ts.map +0 -1
  55. package/dist/cli/types.js +0 -7
  56. package/dist/cli/types.js.map +0 -1
  57. package/dist/cli/utils.d.ts +0 -29
  58. package/dist/cli/utils.d.ts.map +0 -1
  59. package/dist/cli/utils.js +0 -53
  60. package/dist/cli/utils.js.map +0 -1
  61. package/dist/cli.d.ts +0 -9
  62. package/dist/cli.d.ts.map +0 -1
  63. package/dist/cli.js +0 -1805
  64. package/dist/config/generator.d.ts +0 -90
  65. package/dist/config/generator.d.ts.map +0 -1
  66. package/dist/config/generator.js +0 -320
  67. package/dist/config/generator.js.map +0 -1
  68. package/dist/config/loader.d.ts +0 -107
  69. package/dist/config/loader.d.ts.map +0 -1
  70. package/dist/config/loader.js +0 -251
  71. package/dist/config/loader.js.map +0 -1
  72. package/dist/config/schema.d.ts +0 -107
  73. package/dist/config/schema.d.ts.map +0 -1
  74. package/dist/config/schema.js +0 -169
  75. package/dist/config/schema.js.map +0 -1
  76. package/dist/enrich/audio-transcription.d.ts +0 -77
  77. package/dist/enrich/audio-transcription.d.ts.map +0 -1
  78. package/dist/enrich/audio-transcription.js +0 -370
  79. package/dist/enrich/audio-transcription.js.map +0 -1
  80. package/dist/enrich/checkpoint.d.ts +0 -137
  81. package/dist/enrich/checkpoint.d.ts.map +0 -1
  82. package/dist/enrich/checkpoint.js +0 -205
  83. package/dist/enrich/checkpoint.js.map +0 -1
  84. package/dist/enrich/idempotency.d.ts +0 -90
  85. package/dist/enrich/idempotency.d.ts.map +0 -1
  86. package/dist/enrich/idempotency.js +0 -188
  87. package/dist/enrich/idempotency.js.map +0 -1
  88. package/dist/enrich/image-analysis.d.ts +0 -62
  89. package/dist/enrich/image-analysis.d.ts.map +0 -1
  90. package/dist/enrich/image-analysis.js +0 -264
  91. package/dist/enrich/image-analysis.js.map +0 -1
  92. package/dist/enrich/index.d.ts +0 -60
  93. package/dist/enrich/index.d.ts.map +0 -1
  94. package/dist/enrich/index.js +0 -74
  95. package/dist/enrich/index.js.map +0 -1
  96. package/dist/enrich/link-enrichment.d.ts +0 -37
  97. package/dist/enrich/link-enrichment.d.ts.map +0 -1
  98. package/dist/enrich/link-enrichment.js +0 -202
  99. package/dist/enrich/link-enrichment.js.map +0 -1
  100. package/dist/enrich/pdf-video-handling.d.ts +0 -49
  101. package/dist/enrich/pdf-video-handling.d.ts.map +0 -1
  102. package/dist/enrich/pdf-video-handling.js +0 -325
  103. package/dist/enrich/pdf-video-handling.js.map +0 -1
  104. package/dist/enrich/progress-tracker.d.ts +0 -120
  105. package/dist/enrich/progress-tracker.d.ts.map +0 -1
  106. package/dist/enrich/progress-tracker.js +0 -220
  107. package/dist/enrich/progress-tracker.js.map +0 -1
  108. package/dist/enrich/providers/firecrawl.d.ts +0 -18
  109. package/dist/enrich/providers/firecrawl.d.ts.map +0 -1
  110. package/dist/enrich/providers/firecrawl.js +0 -48
  111. package/dist/enrich/providers/firecrawl.js.map +0 -1
  112. package/dist/enrich/providers/generic.d.ts +0 -16
  113. package/dist/enrich/providers/generic.d.ts.map +0 -1
  114. package/dist/enrich/providers/generic.js +0 -36
  115. package/dist/enrich/providers/generic.js.map +0 -1
  116. package/dist/enrich/providers/index.d.ts +0 -14
  117. package/dist/enrich/providers/index.d.ts.map +0 -1
  118. package/dist/enrich/providers/index.js +0 -13
  119. package/dist/enrich/providers/index.js.map +0 -1
  120. package/dist/enrich/providers/instagram.d.ts +0 -16
  121. package/dist/enrich/providers/instagram.d.ts.map +0 -1
  122. package/dist/enrich/providers/instagram.js +0 -43
  123. package/dist/enrich/providers/instagram.js.map +0 -1
  124. package/dist/enrich/providers/spotify.d.ts +0 -16
  125. package/dist/enrich/providers/spotify.d.ts.map +0 -1
  126. package/dist/enrich/providers/spotify.js +0 -45
  127. package/dist/enrich/providers/spotify.js.map +0 -1
  128. package/dist/enrich/providers/twitter.d.ts +0 -16
  129. package/dist/enrich/providers/twitter.d.ts.map +0 -1
  130. package/dist/enrich/providers/twitter.js +0 -43
  131. package/dist/enrich/providers/twitter.js.map +0 -1
  132. package/dist/enrich/providers/types.d.ts +0 -47
  133. package/dist/enrich/providers/types.d.ts.map +0 -1
  134. package/dist/enrich/providers/types.js +0 -15
  135. package/dist/enrich/providers/types.js.map +0 -1
  136. package/dist/enrich/providers/youtube.d.ts +0 -16
  137. package/dist/enrich/providers/youtube.d.ts.map +0 -1
  138. package/dist/enrich/providers/youtube.js +0 -43
  139. package/dist/enrich/providers/youtube.js.map +0 -1
  140. package/dist/enrich/rate-limiting.d.ts +0 -118
  141. package/dist/enrich/rate-limiting.d.ts.map +0 -1
  142. package/dist/enrich/rate-limiting.js +0 -258
  143. package/dist/enrich/rate-limiting.js.map +0 -1
  144. package/dist/index.d.ts.map +0 -1
  145. package/dist/index.js.map +0 -1
  146. package/dist/ingest/dedup-merge.d.ts +0 -82
  147. package/dist/ingest/dedup-merge.d.ts.map +0 -1
  148. package/dist/ingest/dedup-merge.js +0 -262
  149. package/dist/ingest/dedup-merge.js.map +0 -1
  150. package/dist/ingest/ingest-csv.d.ts +0 -62
  151. package/dist/ingest/ingest-csv.d.ts.map +0 -1
  152. package/dist/ingest/ingest-csv.js +0 -300
  153. package/dist/ingest/ingest-csv.js.map +0 -1
  154. package/dist/ingest/ingest-db.d.ts +0 -64
  155. package/dist/ingest/ingest-db.d.ts.map +0 -1
  156. package/dist/ingest/ingest-db.js +0 -172
  157. package/dist/ingest/ingest-db.js.map +0 -1
  158. package/dist/ingest/link-replies-and-tapbacks.d.ts +0 -53
  159. package/dist/ingest/link-replies-and-tapbacks.d.ts.map +0 -1
  160. package/dist/ingest/link-replies-and-tapbacks.js +0 -381
  161. package/dist/ingest/link-replies-and-tapbacks.js.map +0 -1
  162. package/dist/normalize/date-converters.d.ts +0 -45
  163. package/dist/normalize/date-converters.d.ts.map +0 -1
  164. package/dist/normalize/date-converters.js +0 -166
  165. package/dist/normalize/date-converters.js.map +0 -1
  166. package/dist/normalize/path-validator.d.ts +0 -65
  167. package/dist/normalize/path-validator.d.ts.map +0 -1
  168. package/dist/normalize/path-validator.js +0 -221
  169. package/dist/normalize/path-validator.js.map +0 -1
  170. package/dist/normalize/validate-normalized.d.ts +0 -45
  171. package/dist/normalize/validate-normalized.d.ts.map +0 -1
  172. package/dist/normalize/validate-normalized.js +0 -144
  173. package/dist/normalize/validate-normalized.js.map +0 -1
  174. package/dist/render/embeds-blockquotes.d.ts +0 -84
  175. package/dist/render/embeds-blockquotes.d.ts.map +0 -1
  176. package/dist/render/embeds-blockquotes.js +0 -204
  177. package/dist/render/embeds-blockquotes.js.map +0 -1
  178. package/dist/render/grouping.d.ts +0 -78
  179. package/dist/render/grouping.d.ts.map +0 -1
  180. package/dist/render/grouping.js +0 -134
  181. package/dist/render/grouping.js.map +0 -1
  182. package/dist/render/index.d.ts +0 -47
  183. package/dist/render/index.d.ts.map +0 -1
  184. package/dist/render/index.js +0 -245
  185. package/dist/render/index.js.map +0 -1
  186. package/dist/render/reply-rendering.d.ts +0 -88
  187. package/dist/render/reply-rendering.d.ts.map +0 -1
  188. package/dist/render/reply-rendering.js +0 -196
  189. package/dist/render/reply-rendering.js.map +0 -1
  190. package/dist/schema/message.d.ts +0 -125
  191. package/dist/schema/message.d.ts.map +0 -1
  192. package/dist/schema/message.js +0 -331
  193. package/dist/schema/message.js.map +0 -1
  194. package/dist/utils/delta-detection.d.ts +0 -107
  195. package/dist/utils/delta-detection.d.ts.map +0 -1
  196. package/dist/utils/delta-detection.js +0 -199
  197. package/dist/utils/delta-detection.js.map +0 -1
  198. package/dist/utils/enrichment-merge.d.ts +0 -135
  199. package/dist/utils/enrichment-merge.d.ts.map +0 -1
  200. package/dist/utils/enrichment-merge.js +0 -280
  201. package/dist/utils/enrichment-merge.js.map +0 -1
  202. package/dist/utils/human.d.ts +0 -15
  203. package/dist/utils/human.d.ts.map +0 -1
  204. package/dist/utils/human.js +0 -27
  205. package/dist/utils/human.js.map +0 -1
  206. package/dist/utils/incremental-state.d.ts +0 -133
  207. package/dist/utils/incremental-state.d.ts.map +0 -1
  208. package/dist/utils/incremental-state.js +0 -237
  209. package/dist/utils/incremental-state.js.map +0 -1
  210. package/dist/utils/logger.d.ts +0 -40
  211. package/dist/utils/logger.d.ts.map +0 -1
  212. package/dist/utils/logger.js +0 -176
  213. package/dist/utils/logger.js.map +0 -1
@@ -1,262 +0,0 @@
1
- /**
2
- * AC01 + AC02 + AC03 + AC04 + AC05: Main dedup and merge function
3
- *
4
- * Strategy:
5
- * 1. Build GUID index for fast lookup
6
- * 2. For each CSV message:
7
- * a. Try exact GUID match (AC01)
8
- * b. Try content equivalence (AC03)
9
- * c. Apply DB authoritiveness if merging (AC02)
10
- * d. Keep separate if no match
11
- * 3. Add unmatched DB messages
12
- * 4. Verify no data loss (AC04)
13
- * 5. Ensure determinism (AC05)
14
- */
15
- export function dedupAndMerge(csvMessages, dbMessages) {
16
- // AC05: Sort inputs for determinism
17
- const sortedCsv = [...csvMessages].sort((a, b) => a.guid.localeCompare(b.guid));
18
- const sortedDb = [...dbMessages].sort((a, b) => a.guid.localeCompare(b.guid));
19
- const stats = {
20
- csvCount: csvMessages.length,
21
- dbCount: dbMessages.length,
22
- outputCount: 0,
23
- exactMatches: 0,
24
- contentMatches: 0,
25
- conflicts: 0,
26
- noMatches: 0,
27
- };
28
- const outputMessages = [];
29
- const matchedDbGuids = new Set();
30
- // Build O(1) lookup indices for DB messages
31
- const dbByGuid = new Map();
32
- const dbByNormalizedContent = new Map();
33
- for (const dbMsg of sortedDb) {
34
- dbByGuid.set(dbMsg.guid, dbMsg);
35
- // Index by normalized text for content equivalence (text messages only)
36
- if (dbMsg.messageKind === 'text' && dbMsg.text) {
37
- const normalizedKey = `${dbMsg.handle || ''}:${normalizeTextForIndex(dbMsg.text)}`;
38
- const existing = dbByNormalizedContent.get(normalizedKey);
39
- if (existing) {
40
- existing.push(dbMsg);
41
- }
42
- else {
43
- dbByNormalizedContent.set(normalizedKey, [dbMsg]);
44
- }
45
- }
46
- }
47
- // Process each CSV message
48
- for (const csvMsg of sortedCsv) {
49
- // AC01: Try exact GUID match first - O(1) lookup
50
- const exactMatch = dbByGuid.get(csvMsg.guid) || null;
51
- if (exactMatch) {
52
- // AC02: Merge with DB authoritiveness
53
- const merged = applyDbAuthoritiveness(csvMsg, exactMatch);
54
- outputMessages.push(merged);
55
- matchedDbGuids.add(exactMatch.guid);
56
- stats.exactMatches++;
57
- }
58
- else {
59
- // AC03: Try content equivalence using indexed lookup - O(1) average case for text
60
- let contentMatch = detectContentEquivalenceIndexed(csvMsg, dbByNormalizedContent, matchedDbGuids);
61
- // Fallback to linear scan for non-text messages (media, tapback, notification)
62
- // The indexed lookup only handles text messages; media uses media.id comparison
63
- if (!contentMatch && csvMsg.messageKind !== 'text') {
64
- const unmatchedDbMessages = sortedDb.filter((dbMsg) => !matchedDbGuids.has(dbMsg.guid));
65
- contentMatch = detectContentEquivalence(csvMsg, unmatchedDbMessages);
66
- }
67
- if (contentMatch) {
68
- // AC02: Merge with DB authoritiveness
69
- const merged = applyDbAuthoritiveness(csvMsg, contentMatch.message);
70
- outputMessages.push(merged);
71
- matchedDbGuids.add(contentMatch.message.guid);
72
- stats.contentMatches++;
73
- }
74
- else {
75
- // No match found, keep CSV message as-is
76
- outputMessages.push(csvMsg);
77
- stats.noMatches++;
78
- }
79
- }
80
- }
81
- // Add unmatched DB messages
82
- for (const dbMsg of sortedDb) {
83
- if (!matchedDbGuids.has(dbMsg.guid)) {
84
- outputMessages.push(dbMsg);
85
- }
86
- }
87
- stats.outputCount = outputMessages.length;
88
- return {
89
- messages: outputMessages,
90
- stats,
91
- };
92
- }
93
- /**
94
- * AC01: Find exact GUID match in DB messages
95
- */
96
- export function findExactMatch(message, dbMessages) {
97
- return dbMessages.find((dbMsg) => dbMsg.guid === message.guid) || null;
98
- }
99
- /**
100
- * AC03: Detect content equivalence
101
- *
102
- * Normalizes text and compares:
103
- * - Normalized text content (lowercase, trimmed, punctuation removed)
104
- * - messageKind must match
105
- * - sender (handle) must match
106
- *
107
- * Returns match with confidence score (1.0 = exact match)
108
- */
109
- export function detectContentEquivalence(csvMsg, candidates, threshold = 0.9) {
110
- for (const candidate of candidates) {
111
- const reasons = [];
112
- let confidence = 0;
113
- // Must have same messageKind
114
- if (csvMsg.messageKind !== candidate.messageKind) {
115
- continue;
116
- }
117
- // Must have same sender (handle)
118
- const csvHandle = csvMsg.handle || null;
119
- const candidateHandle = candidate.handle || null;
120
- if (csvHandle !== candidateHandle) {
121
- continue;
122
- }
123
- // For text messages, compare normalized text
124
- if (csvMsg.messageKind === 'text' && candidate.messageKind === 'text') {
125
- const csvText = normalizeText(csvMsg.text || '');
126
- const candidateText = normalizeText(candidate.text || '');
127
- if (csvText === candidateText) {
128
- confidence = 1.0;
129
- reasons.push('exact text match after normalization');
130
- }
131
- else {
132
- // Not an exact match, skip to avoid false positives
133
- continue;
134
- }
135
- }
136
- else if (csvMsg.messageKind === 'media' &&
137
- candidate.messageKind === 'media') {
138
- // For media messages, compare media metadata
139
- const csvMediaId = csvMsg.media?.id;
140
- const candidateMediaId = candidate.media?.id;
141
- if (csvMediaId && candidateMediaId && csvMediaId === candidateMediaId) {
142
- confidence = 1.0;
143
- reasons.push('exact media ID match');
144
- }
145
- else {
146
- continue;
147
- }
148
- }
149
- else {
150
- // Other message types - require exact text or skip
151
- continue;
152
- }
153
- // Only return if confidence meets threshold
154
- if (confidence >= threshold) {
155
- return {
156
- message: candidate,
157
- confidence,
158
- reasons,
159
- };
160
- }
161
- }
162
- return null;
163
- }
164
- /**
165
- * Normalize text for content equivalence detection
166
- * - Lowercase
167
- * - Trim whitespace
168
- * - Remove punctuation and extra spaces
169
- */
170
- function normalizeText(text) {
171
- return text
172
- .toLowerCase()
173
- .trim()
174
- .replace(/[^\w\s]/g, '') // Remove punctuation
175
- .replace(/\s+/g, ' ') // Normalize whitespace
176
- .trim();
177
- }
178
- /**
179
- * Normalize text for Map index key (same as normalizeText)
180
- * Used when building the content equivalence index
181
- */
182
- function normalizeTextForIndex(text) {
183
- return normalizeText(text);
184
- }
185
- /**
186
- * AC03: O(1) content equivalence detection using pre-built index
187
- *
188
- * @param csvMsg - CSV message to find match for
189
- * @param contentIndex - Map of normalized content key to DB messages
190
- * @param matchedGuids - Set of already-matched GUIDs to skip
191
- * @returns ContentMatch if found, null otherwise
192
- */
193
- function detectContentEquivalenceIndexed(csvMsg, contentIndex, matchedGuids) {
194
- // Only text messages are indexed
195
- if (csvMsg.messageKind !== 'text' || !csvMsg.text) {
196
- return null;
197
- }
198
- // Build the lookup key
199
- const normalizedKey = `${csvMsg.handle || ''}:${normalizeTextForIndex(csvMsg.text)}`;
200
- const candidates = contentIndex.get(normalizedKey);
201
- if (!candidates || candidates.length === 0) {
202
- return null;
203
- }
204
- // Find first unmatched candidate
205
- for (const candidate of candidates) {
206
- if (!matchedGuids.has(candidate.guid)) {
207
- return {
208
- message: candidate,
209
- confidence: 1.0,
210
- reasons: ['exact text match after normalization (indexed)'],
211
- };
212
- }
213
- }
214
- return null;
215
- }
216
- /**
217
- * AC02: Merge messages with DB authoritiveness
218
- *
219
- * DB is authoritative for:
220
- * - All timestamps (date, dateRead, dateDelivered, dateEdited)
221
- * - Associations (replyingTo.targetMessageGuid)
222
- * - handle
223
- *
224
- * CSV fields are preserved when DB doesn't have them
225
- */
226
- export function applyDbAuthoritiveness(csvMsg, dbMsg) {
227
- // Start with CSV message
228
- const merged = { ...csvMsg };
229
- // DB authoritative: timestamps
230
- merged.date = dbMsg.date;
231
- if (dbMsg.dateRead !== undefined)
232
- merged.dateRead = dbMsg.dateRead;
233
- if (dbMsg.dateDelivered !== undefined)
234
- merged.dateDelivered = dbMsg.dateDelivered;
235
- if (dbMsg.dateEdited !== undefined)
236
- merged.dateEdited = dbMsg.dateEdited;
237
- // DB authoritative: handle
238
- if (dbMsg.handle !== undefined)
239
- merged.handle = dbMsg.handle;
240
- // DB authoritative: associations (replyingTo)
241
- if (dbMsg.replyingTo?.targetMessageGuid !== undefined) {
242
- merged.replyingTo = {
243
- ...merged.replyingTo,
244
- targetMessageGuid: dbMsg.replyingTo.targetMessageGuid,
245
- };
246
- }
247
- // DB authoritative: isRead status
248
- if (dbMsg.isRead !== undefined)
249
- merged.isRead = dbMsg.isRead;
250
- // Prefer DB GUID (stable choice)
251
- merged.guid = dbMsg.guid;
252
- return merged;
253
- }
254
- /**
255
- * AC04: Verify count invariants to prevent data loss
256
- */
257
- export function verifyNoDataLoss(csvCount, dbCount, outputCount) {
258
- // TODO: Implement count verification
259
- // Invariant: outputCount >= max(csvCount, dbCount) - dedup count
260
- return outputCount >= Math.max(csvCount, dbCount);
261
- }
262
- //# sourceMappingURL=dedup-merge.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"dedup-merge.js","sourceRoot":"","sources":["../../src/ingest/dedup-merge.ts"],"names":[],"mappings":"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"}
@@ -1,62 +0,0 @@
1
- import type { ExportEnvelope, Message } from '../schema/message.js';
2
- export type IngestOptions = {
3
- attachmentRoots: string[];
4
- messageDate?: string;
5
- };
6
- export type CSVRow = {
7
- [key: string]: string | undefined;
8
- };
9
- /**
10
- * Main entry point: Ingest CSV file and convert to unified Message schema
11
- */
12
- export declare function ingestCSV(csvFilePath: string, options: IngestOptions): Message[];
13
- /**
14
- * Parse a single CSV row and produce 1-N messages
15
- * Maps iMazing CSV format to unified Message schema
16
- *
17
- * AC01: Parse iMazing CSV rows with correct field mapping per CSV header
18
- */
19
- export declare function parseCSVRow(row: CSVRow, lineNumber: number, options: IngestOptions): Message[];
20
- /**
21
- * Convert CSV date to ISO 8601 UTC with Z suffix
22
- * Input format: "YYYY-MM-DD HH:MM:SS" (space-separated)
23
- */
24
- export declare function convertToISO8601(csvDate: string): string | null;
25
- type AttachmentRecord = {
26
- copied_path?: string;
27
- filename?: string;
28
- senderName?: string;
29
- };
30
- /**
31
- * Resolve attachment path to absolute path when file exists
32
- */
33
- export declare function resolveAttachmentPath(attachment: AttachmentRecord | null | undefined, options: IngestOptions & {
34
- messageDate?: string;
35
- }): string | null;
36
- /**
37
- * Infer media kind from MIME type
38
- */
39
- export declare function inferMediaKind(mimeType: string): 'image' | 'audio' | 'video' | 'pdf' | 'unknown';
40
- /**
41
- * Format ISO 8601 date for attachment search pattern
42
- * Converts: 2023-10-23T06:52:57.000Z → 2023-10-23 06 52 57
43
- */
44
- export declare function formatDateForAttachmentSearch(isoDate: string): string;
45
- /**
46
- * Export envelope wrapper for CSV ingestion output
47
- */
48
- export declare function createExportEnvelope(messages: Message[]): ExportEnvelope;
49
- type ValidationError = {
50
- index: number;
51
- message: Message;
52
- issues: unknown[];
53
- };
54
- /**
55
- * Validate all messages pass schema validation
56
- */
57
- export declare function validateMessages(messages: Message[]): {
58
- valid: boolean;
59
- errors: ValidationError[];
60
- };
61
- export {};
62
- //# sourceMappingURL=ingest-csv.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"ingest-csv.d.ts","sourceRoot":"","sources":["../../src/ingest/ingest-csv.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,cAAc,EAAE,OAAO,EAAE,MAAM,sBAAsB,CAAA;AAGnE,MAAM,MAAM,aAAa,GAAG;IAC3B,eAAe,EAAE,MAAM,EAAE,CAAA;IACzB,WAAW,CAAC,EAAE,MAAM,CAAA;CACpB,CAAA;AAED,MAAM,MAAM,MAAM,GAAG;IACpB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAAA;CACjC,CAAA;AAED;;GAEG;AACH,wBAAgB,SAAS,CACxB,WAAW,EAAE,MAAM,EACnB,OAAO,EAAE,aAAa,GACpB,OAAO,EAAE,CAcX;AAED;;;;;GAKG;AACH,wBAAgB,WAAW,CAC1B,GAAG,EAAE,MAAM,EACX,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,aAAa,GACpB,OAAO,EAAE,CAoIX;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CA+B/D;AAED,KAAK,gBAAgB,GAAG;IACvB,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,UAAU,CAAC,EAAE,MAAM,CAAA;CACnB,CAAA;AAED;;GAEG;AACH,wBAAgB,qBAAqB,CACpC,UAAU,EAAE,gBAAgB,GAAG,IAAI,GAAG,SAAS,EAC/C,OAAO,EAAE,aAAa,GAAG;IAAE,WAAW,CAAC,EAAE,MAAM,CAAA;CAAE,GAC/C,MAAM,GAAG,IAAI,CAsDf;AAED;;GAEG;AACH,wBAAgB,cAAc,CAC7B,QAAQ,EAAE,MAAM,GACd,OAAO,GAAG,OAAO,GAAG,OAAO,GAAG,KAAK,GAAG,SAAS,CASjD;AAED;;;GAGG;AACH,wBAAgB,6BAA6B,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAcrE;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE,OAAO,EAAE,GAAG,cAAc,CAOxE;AAED,KAAK,eAAe,GAAG;IACtB,KAAK,EAAE,MAAM,CAAA;IACb,OAAO,EAAE,OAAO,CAAA;IAChB,MAAM,EAAE,OAAO,EAAE,CAAA;CACjB,CAAA;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,OAAO,EAAE,GAAG;IACtD,KAAK,EAAE,OAAO,CAAA;IACd,MAAM,EAAE,eAAe,EAAE,CAAA;CACzB,CAqBA"}
@@ -1,300 +0,0 @@
1
- import { existsSync, readdirSync, readFileSync } from 'node:fs';
2
- import * as os from 'node:os';
3
- import * as path from 'node:path';
4
- import { parse } from 'csv-parse/sync';
5
- import { MessageSchema } from '../schema/message.js';
6
- /**
7
- * Main entry point: Ingest CSV file and convert to unified Message schema
8
- */
9
- export function ingestCSV(csvFilePath, options) {
10
- const csvContent = readFileSync(csvFilePath, 'utf-8');
11
- const rows = parse(csvContent, { columns: true });
12
- const messages = [];
13
- let lineNumber = 2; // Start at 2 (header is line 1)
14
- for (const row of rows) {
15
- const rowMessages = parseCSVRow(row, lineNumber, options);
16
- messages.push(...rowMessages);
17
- lineNumber++;
18
- }
19
- return messages;
20
- }
21
- /**
22
- * Parse a single CSV row and produce 1-N messages
23
- * Maps iMazing CSV format to unified Message schema
24
- *
25
- * AC01: Parse iMazing CSV rows with correct field mapping per CSV header
26
- */
27
- export function parseCSVRow(row, lineNumber, options) {
28
- const messages = [];
29
- // Extract iMazing CSV fields (headers have spaces, not underscores)
30
- const messageDate = row['Message Date'];
31
- const deliveredDate = row['Delivered Date'];
32
- const readDate = row['Read Date'];
33
- const editedDate = row['Edited Date'];
34
- const service = row.Service;
35
- const type = row.Type;
36
- const senderName = row['Sender Name'];
37
- const senderID = row['Sender ID'];
38
- const status = row.Status;
39
- const text = row.Text;
40
- const subject = row.Subject;
41
- const attachment = row.Attachment;
42
- const attachmentType = row['Attachment type'];
43
- const replyingTo = row['Replying to'];
44
- // AC03: Convert CSV dates to ISO 8601 UTC with Z suffix
45
- const date = convertToISO8601(messageDate || '');
46
- if (!date)
47
- return []; // Skip rows with invalid dates
48
- // AC02: Split rows into text/media/tapback/notification by analyzing content
49
- // Determine messageKind and isFromMe from Type field
50
- const isFromMe = type === 'Outgoing' || type === 'Sent';
51
- let messageKind = 'text';
52
- if (type === 'Notification') {
53
- messageKind = 'notification';
54
- }
55
- // Base message object with common fields
56
- const baseMessage = {
57
- isFromMe,
58
- date,
59
- };
60
- // Conditionally add optional fields to satisfy exactOptionalPropertyTypes
61
- const handle = senderName || senderID;
62
- if (handle)
63
- baseMessage.handle = handle;
64
- if (service)
65
- baseMessage.service = service;
66
- if (subject)
67
- baseMessage.subject = subject;
68
- if (readDate)
69
- baseMessage.dateRead = convertToISO8601(readDate);
70
- if (deliveredDate)
71
- baseMessage.dateDelivered = convertToISO8601(deliveredDate);
72
- if (editedDate) {
73
- baseMessage.dateEdited = convertToISO8601(editedDate);
74
- baseMessage.isEdited = true;
75
- }
76
- if (status === 'Read')
77
- baseMessage.isRead = true;
78
- else if (status === 'Unread')
79
- baseMessage.isRead = false;
80
- // AC05: Preserve row metadata (source, line number) for provenance
81
- const baseExportMetadata = {
82
- source: 'csv',
83
- lineNumber,
84
- csvGuid: `csv:${lineNumber}:0`,
85
- ...(replyingTo && { replyingTo }),
86
- };
87
- // Create text message
88
- if (messageKind === 'text' && text) {
89
- const textMessage = {
90
- ...baseMessage,
91
- guid: `csv:${lineNumber}:0`,
92
- messageKind: 'text',
93
- text,
94
- exportMetadata: baseExportMetadata,
95
- };
96
- messages.push(textMessage);
97
- }
98
- // AC04: Resolve iMazing attachment paths to absolute paths when files exist
99
- if (attachment && attachment.trim() !== '') {
100
- const resolvedPath = resolveAttachmentPath({ filename: attachment }, {
101
- ...options,
102
- messageDate: date,
103
- });
104
- // Only create media message if path can be resolved to absolute path (schema requirement)
105
- if (resolvedPath) {
106
- const mediaMessage = {
107
- ...baseMessage,
108
- guid: `csv:${lineNumber}:0:media`,
109
- messageKind: 'media',
110
- media: {
111
- id: `media:csv:${lineNumber}:0`,
112
- filename: attachment,
113
- path: resolvedPath,
114
- mimeType: attachmentType || undefined,
115
- mediaKind: inferMediaKind(attachmentType || ''),
116
- },
117
- exportMetadata: {
118
- ...baseExportMetadata,
119
- attachmentIndex: 0,
120
- },
121
- };
122
- messages.push(mediaMessage);
123
- }
124
- }
125
- // Create notification message if explicitly marked
126
- if (messageKind === 'notification') {
127
- const notificationMessage = {
128
- ...baseMessage,
129
- guid: `csv:${lineNumber}:0`,
130
- messageKind: 'notification',
131
- exportMetadata: baseExportMetadata,
132
- };
133
- messages.push(notificationMessage);
134
- }
135
- // Fallback: If no messages created but we have text, create text message
136
- if (messages.length === 0 && text) {
137
- const fallbackMessage = {
138
- ...baseMessage,
139
- guid: `csv:${lineNumber}:0`,
140
- messageKind: 'text',
141
- text,
142
- exportMetadata: baseExportMetadata,
143
- };
144
- messages.push(fallbackMessage);
145
- }
146
- return messages;
147
- }
148
- /**
149
- * Convert CSV date to ISO 8601 UTC with Z suffix
150
- * Input format: "YYYY-MM-DD HH:MM:SS" (space-separated)
151
- */
152
- export function convertToISO8601(csvDate) {
153
- if (!csvDate || csvDate.trim() === '') {
154
- return null;
155
- }
156
- try {
157
- // Normalize: convert space to T (CSV uses space, ISO 8601 uses T)
158
- const normalized = csvDate.trim().replace(' ', 'T');
159
- // Basic validation: should contain date separators
160
- if (!normalized.includes('-') && !normalized.includes('/')) {
161
- return null;
162
- }
163
- // Append Z if not present (assuming UTC)
164
- let isoString = normalized;
165
- if (!normalized.includes('Z') && !normalized.match(/[+-]\d{2}:/)) {
166
- isoString = `${normalized}Z`;
167
- }
168
- // Parse and validate
169
- const date = new Date(isoString);
170
- if (Number.isNaN(date.getTime())) {
171
- return null;
172
- }
173
- // Return ISO 8601 with Z suffix
174
- return date.toISOString();
175
- }
176
- catch {
177
- return null;
178
- }
179
- }
180
- /**
181
- * Resolve attachment path to absolute path when file exists
182
- */
183
- export function resolveAttachmentPath(attachment, options) {
184
- if (!attachment)
185
- return null;
186
- const { attachmentRoots, messageDate } = options;
187
- // If already absolute and exists, return it
188
- if (attachment.copied_path?.startsWith('/')) {
189
- if (existsSync(attachment.copied_path)) {
190
- return attachment.copied_path;
191
- }
192
- }
193
- // Expand tilde if present
194
- if (attachment.copied_path?.startsWith('~')) {
195
- const expanded = attachment.copied_path.replace('~', os.homedir());
196
- if (existsSync(expanded)) {
197
- return expanded;
198
- }
199
- }
200
- // Search using timestamp pattern in attachment roots
201
- if (messageDate && attachmentRoots.length > 0) {
202
- const dateStr = formatDateForAttachmentSearch(messageDate);
203
- const filename = attachment.filename || 'unknown';
204
- const senderName = attachment.senderName || '*';
205
- for (const root of attachmentRoots) {
206
- // Try exact pattern: YYYY-MM-DD HH MM SS - SenderName - filename
207
- const pattern = `${dateStr} - ${senderName} - ${filename}`;
208
- const fullPath = path.join(root, pattern);
209
- if (existsSync(fullPath)) {
210
- return fullPath;
211
- }
212
- // Try wildcard pattern if sender name unknown
213
- if (senderName === '*' && existsSync(root)) {
214
- try {
215
- const files = readdirSync(root).filter((f) => {
216
- return f.includes(dateStr) && f.endsWith(filename);
217
- });
218
- if (files.length > 0 && files[0]) {
219
- return path.join(root, files[0]);
220
- }
221
- }
222
- catch {
223
- // Directory doesn't exist or can't be read
224
- }
225
- }
226
- }
227
- }
228
- // Not found
229
- return null;
230
- }
231
- /**
232
- * Infer media kind from MIME type
233
- */
234
- export function inferMediaKind(mimeType) {
235
- if (!mimeType)
236
- return 'unknown';
237
- if (mimeType.startsWith('image/'))
238
- return 'image';
239
- if (mimeType.startsWith('audio/'))
240
- return 'audio';
241
- if (mimeType.startsWith('video/'))
242
- return 'video';
243
- if (mimeType.includes('pdf'))
244
- return 'pdf';
245
- return 'unknown';
246
- }
247
- /**
248
- * Format ISO 8601 date for attachment search pattern
249
- * Converts: 2023-10-23T06:52:57.000Z → 2023-10-23 06 52 57
250
- */
251
- export function formatDateForAttachmentSearch(isoDate) {
252
- try {
253
- const date = new Date(isoDate);
254
- const year = date.getUTCFullYear();
255
- const month = String(date.getUTCMonth() + 1).padStart(2, '0');
256
- const day = String(date.getUTCDate()).padStart(2, '0');
257
- const hours = String(date.getUTCHours()).padStart(2, '0');
258
- const minutes = String(date.getUTCMinutes()).padStart(2, '0');
259
- const seconds = String(date.getUTCSeconds()).padStart(2, '0');
260
- return `${year}-${month}-${day} ${hours} ${minutes} ${seconds}`;
261
- }
262
- catch {
263
- return '';
264
- }
265
- }
266
- /**
267
- * Export envelope wrapper for CSV ingestion output
268
- */
269
- export function createExportEnvelope(messages) {
270
- return {
271
- schemaVersion: '2.0.0',
272
- source: 'csv',
273
- createdAt: new Date().toISOString(),
274
- messages,
275
- };
276
- }
277
- /**
278
- * Validate all messages pass schema validation
279
- */
280
- export function validateMessages(messages) {
281
- const errors = [];
282
- for (let i = 0; i < messages.length; i++) {
283
- const message = messages[i];
284
- if (!message)
285
- continue;
286
- const result = MessageSchema.safeParse(message);
287
- if (!result.success) {
288
- errors.push({
289
- index: i,
290
- message,
291
- issues: result.error.issues,
292
- });
293
- }
294
- }
295
- return {
296
- valid: errors.length === 0,
297
- errors,
298
- };
299
- }
300
- //# sourceMappingURL=ingest-csv.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"ingest-csv.js","sourceRoot":"","sources":["../../src/ingest/ingest-csv.ts"],"names":[],"mappings":"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"}