@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.
- package/CHANGELOG.md +6 -0
- package/dist/bin/index.js +1 -1
- package/dist/index.js +1 -1
- package/package.json +1 -1
- package/dist/cli/commands/clean.d.ts +0 -17
- package/dist/cli/commands/clean.d.ts.map +0 -1
- package/dist/cli/commands/clean.js +0 -142
- package/dist/cli/commands/clean.js.map +0 -1
- package/dist/cli/commands/doctor.d.ts +0 -17
- package/dist/cli/commands/doctor.d.ts.map +0 -1
- package/dist/cli/commands/doctor.js +0 -202
- package/dist/cli/commands/doctor.js.map +0 -1
- package/dist/cli/commands/enrich-ai.d.ts +0 -17
- package/dist/cli/commands/enrich-ai.d.ts.map +0 -1
- package/dist/cli/commands/enrich-ai.js +0 -371
- package/dist/cli/commands/enrich-ai.js.map +0 -1
- package/dist/cli/commands/index.d.ts +0 -16
- package/dist/cli/commands/index.d.ts.map +0 -1
- package/dist/cli/commands/index.js +0 -16
- package/dist/cli/commands/index.js.map +0 -1
- package/dist/cli/commands/ingest-csv.d.ts +0 -17
- package/dist/cli/commands/ingest-csv.d.ts.map +0 -1
- package/dist/cli/commands/ingest-csv.js +0 -138
- package/dist/cli/commands/ingest-csv.js.map +0 -1
- package/dist/cli/commands/ingest-db.d.ts +0 -17
- package/dist/cli/commands/ingest-db.d.ts.map +0 -1
- package/dist/cli/commands/ingest-db.js +0 -159
- package/dist/cli/commands/ingest-db.js.map +0 -1
- package/dist/cli/commands/init.d.ts +0 -17
- package/dist/cli/commands/init.d.ts.map +0 -1
- package/dist/cli/commands/init.js +0 -110
- package/dist/cli/commands/init.js.map +0 -1
- package/dist/cli/commands/normalize-link.d.ts +0 -16
- package/dist/cli/commands/normalize-link.d.ts.map +0 -1
- package/dist/cli/commands/normalize-link.js +0 -144
- package/dist/cli/commands/normalize-link.js.map +0 -1
- package/dist/cli/commands/render-markdown.d.ts +0 -17
- package/dist/cli/commands/render-markdown.d.ts.map +0 -1
- package/dist/cli/commands/render-markdown.js +0 -218
- package/dist/cli/commands/render-markdown.js.map +0 -1
- package/dist/cli/commands/stats.d.ts +0 -17
- package/dist/cli/commands/stats.d.ts.map +0 -1
- package/dist/cli/commands/stats.js +0 -175
- package/dist/cli/commands/stats.js.map +0 -1
- package/dist/cli/commands/validate.d.ts +0 -17
- package/dist/cli/commands/validate.d.ts.map +0 -1
- package/dist/cli/commands/validate.js +0 -152
- package/dist/cli/commands/validate.js.map +0 -1
- package/dist/cli/index.d.ts +0 -13
- package/dist/cli/index.d.ts.map +0 -1
- package/dist/cli/index.js +0 -121
- package/dist/cli/index.js.map +0 -1
- package/dist/cli/types.d.ts +0 -93
- package/dist/cli/types.d.ts.map +0 -1
- package/dist/cli/types.js +0 -7
- package/dist/cli/types.js.map +0 -1
- package/dist/cli/utils.d.ts +0 -29
- package/dist/cli/utils.d.ts.map +0 -1
- package/dist/cli/utils.js +0 -53
- package/dist/cli/utils.js.map +0 -1
- package/dist/cli.d.ts +0 -9
- package/dist/cli.d.ts.map +0 -1
- package/dist/cli.js +0 -1805
- package/dist/config/generator.d.ts +0 -90
- package/dist/config/generator.d.ts.map +0 -1
- package/dist/config/generator.js +0 -320
- package/dist/config/generator.js.map +0 -1
- package/dist/config/loader.d.ts +0 -107
- package/dist/config/loader.d.ts.map +0 -1
- package/dist/config/loader.js +0 -251
- package/dist/config/loader.js.map +0 -1
- package/dist/config/schema.d.ts +0 -107
- package/dist/config/schema.d.ts.map +0 -1
- package/dist/config/schema.js +0 -169
- package/dist/config/schema.js.map +0 -1
- package/dist/enrich/audio-transcription.d.ts +0 -77
- package/dist/enrich/audio-transcription.d.ts.map +0 -1
- package/dist/enrich/audio-transcription.js +0 -370
- package/dist/enrich/audio-transcription.js.map +0 -1
- package/dist/enrich/checkpoint.d.ts +0 -137
- package/dist/enrich/checkpoint.d.ts.map +0 -1
- package/dist/enrich/checkpoint.js +0 -205
- package/dist/enrich/checkpoint.js.map +0 -1
- package/dist/enrich/idempotency.d.ts +0 -90
- package/dist/enrich/idempotency.d.ts.map +0 -1
- package/dist/enrich/idempotency.js +0 -188
- package/dist/enrich/idempotency.js.map +0 -1
- package/dist/enrich/image-analysis.d.ts +0 -62
- package/dist/enrich/image-analysis.d.ts.map +0 -1
- package/dist/enrich/image-analysis.js +0 -264
- package/dist/enrich/image-analysis.js.map +0 -1
- package/dist/enrich/index.d.ts +0 -60
- package/dist/enrich/index.d.ts.map +0 -1
- package/dist/enrich/index.js +0 -74
- package/dist/enrich/index.js.map +0 -1
- package/dist/enrich/link-enrichment.d.ts +0 -37
- package/dist/enrich/link-enrichment.d.ts.map +0 -1
- package/dist/enrich/link-enrichment.js +0 -202
- package/dist/enrich/link-enrichment.js.map +0 -1
- package/dist/enrich/pdf-video-handling.d.ts +0 -49
- package/dist/enrich/pdf-video-handling.d.ts.map +0 -1
- package/dist/enrich/pdf-video-handling.js +0 -325
- package/dist/enrich/pdf-video-handling.js.map +0 -1
- package/dist/enrich/progress-tracker.d.ts +0 -120
- package/dist/enrich/progress-tracker.d.ts.map +0 -1
- package/dist/enrich/progress-tracker.js +0 -220
- package/dist/enrich/progress-tracker.js.map +0 -1
- package/dist/enrich/providers/firecrawl.d.ts +0 -18
- package/dist/enrich/providers/firecrawl.d.ts.map +0 -1
- package/dist/enrich/providers/firecrawl.js +0 -48
- package/dist/enrich/providers/firecrawl.js.map +0 -1
- package/dist/enrich/providers/generic.d.ts +0 -16
- package/dist/enrich/providers/generic.d.ts.map +0 -1
- package/dist/enrich/providers/generic.js +0 -36
- package/dist/enrich/providers/generic.js.map +0 -1
- package/dist/enrich/providers/index.d.ts +0 -14
- package/dist/enrich/providers/index.d.ts.map +0 -1
- package/dist/enrich/providers/index.js +0 -13
- package/dist/enrich/providers/index.js.map +0 -1
- package/dist/enrich/providers/instagram.d.ts +0 -16
- package/dist/enrich/providers/instagram.d.ts.map +0 -1
- package/dist/enrich/providers/instagram.js +0 -43
- package/dist/enrich/providers/instagram.js.map +0 -1
- package/dist/enrich/providers/spotify.d.ts +0 -16
- package/dist/enrich/providers/spotify.d.ts.map +0 -1
- package/dist/enrich/providers/spotify.js +0 -45
- package/dist/enrich/providers/spotify.js.map +0 -1
- package/dist/enrich/providers/twitter.d.ts +0 -16
- package/dist/enrich/providers/twitter.d.ts.map +0 -1
- package/dist/enrich/providers/twitter.js +0 -43
- package/dist/enrich/providers/twitter.js.map +0 -1
- package/dist/enrich/providers/types.d.ts +0 -47
- package/dist/enrich/providers/types.d.ts.map +0 -1
- package/dist/enrich/providers/types.js +0 -15
- package/dist/enrich/providers/types.js.map +0 -1
- package/dist/enrich/providers/youtube.d.ts +0 -16
- package/dist/enrich/providers/youtube.d.ts.map +0 -1
- package/dist/enrich/providers/youtube.js +0 -43
- package/dist/enrich/providers/youtube.js.map +0 -1
- package/dist/enrich/rate-limiting.d.ts +0 -118
- package/dist/enrich/rate-limiting.d.ts.map +0 -1
- package/dist/enrich/rate-limiting.js +0 -258
- package/dist/enrich/rate-limiting.js.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/ingest/dedup-merge.d.ts +0 -82
- package/dist/ingest/dedup-merge.d.ts.map +0 -1
- package/dist/ingest/dedup-merge.js +0 -262
- package/dist/ingest/dedup-merge.js.map +0 -1
- package/dist/ingest/ingest-csv.d.ts +0 -62
- package/dist/ingest/ingest-csv.d.ts.map +0 -1
- package/dist/ingest/ingest-csv.js +0 -300
- package/dist/ingest/ingest-csv.js.map +0 -1
- package/dist/ingest/ingest-db.d.ts +0 -64
- package/dist/ingest/ingest-db.d.ts.map +0 -1
- package/dist/ingest/ingest-db.js +0 -172
- package/dist/ingest/ingest-db.js.map +0 -1
- package/dist/ingest/link-replies-and-tapbacks.d.ts +0 -53
- package/dist/ingest/link-replies-and-tapbacks.d.ts.map +0 -1
- package/dist/ingest/link-replies-and-tapbacks.js +0 -381
- package/dist/ingest/link-replies-and-tapbacks.js.map +0 -1
- package/dist/normalize/date-converters.d.ts +0 -45
- package/dist/normalize/date-converters.d.ts.map +0 -1
- package/dist/normalize/date-converters.js +0 -166
- package/dist/normalize/date-converters.js.map +0 -1
- package/dist/normalize/path-validator.d.ts +0 -65
- package/dist/normalize/path-validator.d.ts.map +0 -1
- package/dist/normalize/path-validator.js +0 -221
- package/dist/normalize/path-validator.js.map +0 -1
- package/dist/normalize/validate-normalized.d.ts +0 -45
- package/dist/normalize/validate-normalized.d.ts.map +0 -1
- package/dist/normalize/validate-normalized.js +0 -144
- package/dist/normalize/validate-normalized.js.map +0 -1
- package/dist/render/embeds-blockquotes.d.ts +0 -84
- package/dist/render/embeds-blockquotes.d.ts.map +0 -1
- package/dist/render/embeds-blockquotes.js +0 -204
- package/dist/render/embeds-blockquotes.js.map +0 -1
- package/dist/render/grouping.d.ts +0 -78
- package/dist/render/grouping.d.ts.map +0 -1
- package/dist/render/grouping.js +0 -134
- package/dist/render/grouping.js.map +0 -1
- package/dist/render/index.d.ts +0 -47
- package/dist/render/index.d.ts.map +0 -1
- package/dist/render/index.js +0 -245
- package/dist/render/index.js.map +0 -1
- package/dist/render/reply-rendering.d.ts +0 -88
- package/dist/render/reply-rendering.d.ts.map +0 -1
- package/dist/render/reply-rendering.js +0 -196
- package/dist/render/reply-rendering.js.map +0 -1
- package/dist/schema/message.d.ts +0 -125
- package/dist/schema/message.d.ts.map +0 -1
- package/dist/schema/message.js +0 -331
- package/dist/schema/message.js.map +0 -1
- package/dist/utils/delta-detection.d.ts +0 -107
- package/dist/utils/delta-detection.d.ts.map +0 -1
- package/dist/utils/delta-detection.js +0 -199
- package/dist/utils/delta-detection.js.map +0 -1
- package/dist/utils/enrichment-merge.d.ts +0 -135
- package/dist/utils/enrichment-merge.d.ts.map +0 -1
- package/dist/utils/enrichment-merge.js +0 -280
- package/dist/utils/enrichment-merge.js.map +0 -1
- package/dist/utils/human.d.ts +0 -15
- package/dist/utils/human.d.ts.map +0 -1
- package/dist/utils/human.js +0 -27
- package/dist/utils/human.js.map +0 -1
- package/dist/utils/incremental-state.d.ts +0 -133
- package/dist/utils/incremental-state.d.ts.map +0 -1
- package/dist/utils/incremental-state.js +0 -237
- package/dist/utils/incremental-state.js.map +0 -1
- package/dist/utils/logger.d.ts +0 -40
- package/dist/utils/logger.d.ts.map +0 -1
- package/dist/utils/logger.js +0 -176
- 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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"}
|