email-origin-chain 1.0.7 → 1.0.10
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/dist/detectors/crisp-detector.js +5 -0
- package/dist/detectors/new-outlook-detector.js +1 -0
- package/dist/detectors/outlook-empty-header-detector.d.ts +1 -1
- package/dist/detectors/outlook-empty-header-detector.js +2 -1
- package/dist/detectors/outlook-fr-detector.js +1 -0
- package/dist/detectors/outlook-reverse-fr-detector.d.ts +1 -1
- package/dist/detectors/outlook-reverse-fr-detector.js +2 -1
- package/dist/detectors/registry.js +6 -6
- package/dist/detectors/types.d.ts +4 -0
- package/dist/index.js +10 -1
- package/dist/inline-layer.js +15 -1
- package/dist/mime-layer.d.ts +1 -0
- package/dist/mime-layer.js +5 -0
- package/dist/types.d.ts +2 -0
- package/dist/utils.js +20 -1
- package/package.json +1 -1
|
@@ -30,10 +30,15 @@ class CrispDetector {
|
|
|
30
30
|
: from
|
|
31
31
|
? { name: from.name || '', address: from.address || '' }
|
|
32
32
|
: '';
|
|
33
|
+
const to = result.email.to;
|
|
34
|
+
const toValue = Array.isArray(to) && to.length > 0
|
|
35
|
+
? (typeof to[0] === 'string' ? to[0] : { name: to[0].name || '', address: to[0].address || '' })
|
|
36
|
+
: (typeof to === 'string' ? to : undefined);
|
|
33
37
|
return {
|
|
34
38
|
found: true,
|
|
35
39
|
email: {
|
|
36
40
|
from: fromValue,
|
|
41
|
+
to: toValue,
|
|
37
42
|
subject: result.email.subject || undefined,
|
|
38
43
|
date: result.email.date || undefined,
|
|
39
44
|
body: result.email.body || undefined
|
|
@@ -100,6 +100,7 @@ class NewOutlookDetector {
|
|
|
100
100
|
found: true,
|
|
101
101
|
email: {
|
|
102
102
|
from: address ? { name: name.replace(/["']/g, ''), address: address } : name,
|
|
103
|
+
to: to ? to.value : undefined,
|
|
103
104
|
subject: subject.value,
|
|
104
105
|
date: date ? date.value : undefined,
|
|
105
106
|
body: finalBody
|
|
@@ -10,7 +10,7 @@ import { ForwardDetector, DetectionResult } from './types';
|
|
|
10
10
|
*/
|
|
11
11
|
export declare class OutlookEmptyHeaderDetector implements ForwardDetector {
|
|
12
12
|
readonly name = "outlook_empty_header";
|
|
13
|
-
readonly priority = 50;
|
|
13
|
+
readonly priority = -50;
|
|
14
14
|
private readonly HEADER_PATTERN;
|
|
15
15
|
detect(text: string): DetectionResult;
|
|
16
16
|
}
|
|
@@ -14,7 +14,7 @@ const cleaner_1 = require("../utils/cleaner");
|
|
|
14
14
|
class OutlookEmptyHeaderDetector {
|
|
15
15
|
constructor() {
|
|
16
16
|
this.name = 'outlook_empty_header';
|
|
17
|
-
this.priority = 50; //
|
|
17
|
+
this.priority = -50; // Very specific - High Priority
|
|
18
18
|
// Regex to capture the header block:
|
|
19
19
|
// 1. Optional Separator (mostly underscores)
|
|
20
20
|
// 2. De: ... (From)
|
|
@@ -51,6 +51,7 @@ class OutlookEmptyHeaderDetector {
|
|
|
51
51
|
message: message || undefined,
|
|
52
52
|
email: {
|
|
53
53
|
from: fromLine,
|
|
54
|
+
to: toLine,
|
|
54
55
|
subject: subjectLine,
|
|
55
56
|
date: dateLine || undefined,
|
|
56
57
|
body: finalBody
|
|
@@ -107,6 +107,7 @@ class OutlookFRDetector {
|
|
|
107
107
|
from: fromEmail.includes('@')
|
|
108
108
|
? { name: fromName !== fromEmail ? fromName : '', address: fromEmail }
|
|
109
109
|
: { name: fromName, address: fromName },
|
|
110
|
+
to: a ? extractValue(a.line) : undefined,
|
|
110
111
|
subject,
|
|
111
112
|
date: dateRaw,
|
|
112
113
|
body: finalBody
|
|
@@ -4,7 +4,7 @@ import { ForwardDetector, DetectionResult } from './types';
|
|
|
4
4
|
*/
|
|
5
5
|
export declare class OutlookReverseFrDetector implements ForwardDetector {
|
|
6
6
|
readonly name = "outlook_reverse_fr";
|
|
7
|
-
readonly priority = -
|
|
7
|
+
readonly priority = -45;
|
|
8
8
|
private readonly ENVOYE_PATTERN;
|
|
9
9
|
private readonly DE_PATTERN;
|
|
10
10
|
private readonly A_PATTERN;
|
|
@@ -8,7 +8,7 @@ const cleaner_1 = require("../utils/cleaner");
|
|
|
8
8
|
class OutlookReverseFrDetector {
|
|
9
9
|
constructor() {
|
|
10
10
|
this.name = 'outlook_reverse_fr';
|
|
11
|
-
this.priority = -
|
|
11
|
+
this.priority = -45; // Specific detector - High Priority
|
|
12
12
|
// Regex patterns for field detection
|
|
13
13
|
this.ENVOYE_PATTERN = /^[ \t]*Envoy(?:é|=E9|e)?\s*:\s*(.*?)\s*$/m;
|
|
14
14
|
this.DE_PATTERN = /^[ \t]*De\s*:/i;
|
|
@@ -76,6 +76,7 @@ class OutlookReverseFrDetector {
|
|
|
76
76
|
from: fromEmail.includes('@')
|
|
77
77
|
? { name: fromName !== fromEmail ? fromName : '', address: fromEmail }
|
|
78
78
|
: { name: fromName, address: fromName },
|
|
79
|
+
to: a ? extractValue(a.line) : undefined,
|
|
79
80
|
subject: objet ? extractValue(objet.line) : '',
|
|
80
81
|
date: extractValue(envoyeMatch[0]),
|
|
81
82
|
body: finalBody
|
|
@@ -15,12 +15,12 @@ class DetectorRegistry {
|
|
|
15
15
|
constructor(customDetectors = []) {
|
|
16
16
|
this.detectors = [];
|
|
17
17
|
// Register all detectors (priority determines order)
|
|
18
|
-
this.register(new
|
|
19
|
-
this.register(new
|
|
20
|
-
this.register(new
|
|
21
|
-
this.register(new
|
|
22
|
-
this.register(new
|
|
23
|
-
this.register(new
|
|
18
|
+
this.register(new outlook_empty_header_detector_1.OutlookEmptyHeaderDetector()); // priority: -50 (Very specific)
|
|
19
|
+
this.register(new outlook_reverse_fr_detector_1.OutlookReverseFrDetector()); // priority: -45 (Specific)
|
|
20
|
+
this.register(new new_outlook_detector_1.NewOutlookDetector()); // priority: -40 (Specific)
|
|
21
|
+
this.register(new outlook_fr_detector_1.OutlookFRDetector()); // priority: -30 (Fallback for FR)
|
|
22
|
+
this.register(new reply_detector_1.ReplyDetector()); // priority: -10 (Replies)
|
|
23
|
+
this.register(new crisp_detector_1.CrispDetector()); // priority: 100 (Universal fallback)
|
|
24
24
|
// Register custom detectors
|
|
25
25
|
customDetectors.forEach(detector => this.register(detector));
|
|
26
26
|
}
|
package/dist/index.js
CHANGED
|
@@ -53,15 +53,19 @@ async function extractDeepestHybrid(raw, options) {
|
|
|
53
53
|
const inlineResult = await (0, inline_layer_1.processInline)(mimeResult.rawBody, mimeResult.depth, mimeResult.history, opts.customDetectors);
|
|
54
54
|
// Step 3: Align results
|
|
55
55
|
let from = (0, utils_1.normalizeFrom)(inlineResult.from);
|
|
56
|
+
let to = (0, utils_1.normalizeFrom)(inlineResult.to);
|
|
56
57
|
let subject = inlineResult.subject;
|
|
57
58
|
let date_raw = inlineResult.date_raw;
|
|
58
59
|
let date_iso = inlineResult.date_iso;
|
|
59
60
|
let text = inlineResult.text;
|
|
60
61
|
if (inlineResult.diagnostics.method === 'fallback' && mimeResult.metadata) {
|
|
61
62
|
const m = mimeResult.metadata;
|
|
62
|
-
if (!from && m.from?.value?.[0]) {
|
|
63
|
+
if ((!from || !from.address) && m.from?.value?.[0]) {
|
|
63
64
|
from = (0, utils_1.normalizeFrom)({ name: m.from.value[0].name, address: m.from.value[0].address });
|
|
64
65
|
}
|
|
66
|
+
if ((!to || !to.address) && m.to?.value?.[0]) {
|
|
67
|
+
to = (0, utils_1.normalizeFrom)({ name: m.to.value[0].name, address: m.to.value[0].address });
|
|
68
|
+
}
|
|
65
69
|
if (!subject && m.subject)
|
|
66
70
|
subject = m.subject;
|
|
67
71
|
if (!date_iso && m.date)
|
|
@@ -79,6 +83,9 @@ async function extractDeepestHybrid(raw, options) {
|
|
|
79
83
|
if (m.from?.value?.[0]) {
|
|
80
84
|
rootInHistory.from = (0, utils_1.normalizeFrom)({ name: m.from.value[0].name, address: m.from.value[0].address });
|
|
81
85
|
}
|
|
86
|
+
if (m.to?.value?.[0]) {
|
|
87
|
+
rootInHistory.to = (0, utils_1.normalizeFrom)({ name: m.to.value[0].name, address: m.to.value[0].address });
|
|
88
|
+
}
|
|
82
89
|
if (m.subject)
|
|
83
90
|
rootInHistory.subject = m.subject;
|
|
84
91
|
}
|
|
@@ -96,6 +103,7 @@ async function extractDeepestHybrid(raw, options) {
|
|
|
96
103
|
...restInlineResult,
|
|
97
104
|
// Use our normalized/enriched values
|
|
98
105
|
from,
|
|
106
|
+
to,
|
|
99
107
|
subject,
|
|
100
108
|
date_raw,
|
|
101
109
|
date_iso,
|
|
@@ -115,6 +123,7 @@ async function extractDeepestHybrid(raw, options) {
|
|
|
115
123
|
catch (error) {
|
|
116
124
|
return {
|
|
117
125
|
from: null,
|
|
126
|
+
to: null,
|
|
118
127
|
subject: null,
|
|
119
128
|
date_raw: null,
|
|
120
129
|
date_iso: null,
|
package/dist/inline-layer.js
CHANGED
|
@@ -19,6 +19,7 @@ async function processInline(text, depth, baseHistory = [], customDetectors = []
|
|
|
19
19
|
if (history.length === 0) {
|
|
20
20
|
history.push({
|
|
21
21
|
from: null,
|
|
22
|
+
to: null,
|
|
22
23
|
subject: null,
|
|
23
24
|
date_raw: null,
|
|
24
25
|
date_iso: null,
|
|
@@ -77,10 +78,21 @@ async function processInline(text, depth, baseHistory = [], customDetectors = []
|
|
|
77
78
|
? { name: email.from.name, address: email.from.address }
|
|
78
79
|
: (email.from ? { address: email.from } : null);
|
|
79
80
|
fromNormalized = (0, utils_1.normalizeFrom)(fromNormalized);
|
|
81
|
+
// Normalize to address
|
|
82
|
+
let toNormalized = typeof email.to === 'object'
|
|
83
|
+
? { name: email.to.name, address: email.to.address }
|
|
84
|
+
: (email.to ? { address: email.to } : null);
|
|
85
|
+
if (typeof email.to === 'string') {
|
|
86
|
+
toNormalized = (0, utils_1.normalizeFrom)({ address: email.to });
|
|
87
|
+
}
|
|
88
|
+
else if (toNormalized) {
|
|
89
|
+
toNormalized = (0, utils_1.normalizeFrom)(toNormalized);
|
|
90
|
+
}
|
|
80
91
|
// Add this forward level to history
|
|
81
92
|
const cleanedBody = (0, utils_1.cleanText)(email.body || '');
|
|
82
93
|
history.push({
|
|
83
94
|
from: fromNormalized,
|
|
95
|
+
to: toNormalized,
|
|
84
96
|
subject: email.subject || null,
|
|
85
97
|
date_raw: email.date || null,
|
|
86
98
|
date_iso: dateIso,
|
|
@@ -105,10 +117,11 @@ async function processInline(text, depth, baseHistory = [], customDetectors = []
|
|
|
105
117
|
date_raw: deepestEntry.date_raw,
|
|
106
118
|
date_iso: deepestEntry.date_iso,
|
|
107
119
|
text: deepestEntry.text,
|
|
120
|
+
to: deepestEntry.to,
|
|
108
121
|
attachments: [],
|
|
109
122
|
history: history.slice().reverse(),
|
|
110
123
|
diagnostics: {
|
|
111
|
-
method: (deepestEntry.flags.find(f => f.startsWith('method:')) || 'inline'),
|
|
124
|
+
method: (deepestEntry.flags.find(f => f.startsWith('method:'))?.replace('method:', '') || 'inline'),
|
|
112
125
|
depth: currentDepth - startingDepth,
|
|
113
126
|
parsedOk: true,
|
|
114
127
|
warnings: warnings
|
|
@@ -123,6 +136,7 @@ async function processInline(text, depth, baseHistory = [], customDetectors = []
|
|
|
123
136
|
date_raw: currentEntry.date_raw,
|
|
124
137
|
date_iso: currentEntry.date_iso,
|
|
125
138
|
text: currentEntry.text || (0, utils_1.cleanText)(currentText),
|
|
139
|
+
to: currentEntry.to,
|
|
126
140
|
attachments: [],
|
|
127
141
|
history: history.slice().reverse(),
|
|
128
142
|
diagnostics: {
|
package/dist/mime-layer.d.ts
CHANGED
package/dist/mime-layer.js
CHANGED
|
@@ -23,6 +23,10 @@ async function processMime(raw, options) {
|
|
|
23
23
|
name: parsed.from.value[0].name,
|
|
24
24
|
address: parsed.from.value[0].address
|
|
25
25
|
} : null,
|
|
26
|
+
to: parsed.to?.value?.[0] ? {
|
|
27
|
+
name: parsed.to.value[0].name,
|
|
28
|
+
address: parsed.to.value[0].address
|
|
29
|
+
} : null,
|
|
26
30
|
subject: parsed.subject || null,
|
|
27
31
|
date_raw: parsed.date?.toString() || null,
|
|
28
32
|
date_iso: parsed.date ? parsed.date.toISOString() : null,
|
|
@@ -56,6 +60,7 @@ async function processMime(raw, options) {
|
|
|
56
60
|
history,
|
|
57
61
|
metadata: {
|
|
58
62
|
from: parsed.from,
|
|
63
|
+
to: parsed.to,
|
|
59
64
|
subject: parsed.subject,
|
|
60
65
|
date: parsed.date
|
|
61
66
|
}
|
package/dist/types.d.ts
CHANGED
|
@@ -18,6 +18,7 @@ export interface Diagnostics {
|
|
|
18
18
|
}
|
|
19
19
|
export interface HistoryEntry {
|
|
20
20
|
from: EmailAddress | null;
|
|
21
|
+
to: EmailAddress | null;
|
|
21
22
|
subject: string | null;
|
|
22
23
|
date_raw: string | null;
|
|
23
24
|
date_iso: string | null;
|
|
@@ -28,6 +29,7 @@ export interface HistoryEntry {
|
|
|
28
29
|
}
|
|
29
30
|
export interface ResultObject {
|
|
30
31
|
from: EmailAddress | null;
|
|
32
|
+
to: EmailAddress | null;
|
|
31
33
|
subject: string | null;
|
|
32
34
|
date_raw: string | null;
|
|
33
35
|
date_iso: string | null;
|
package/dist/utils.js
CHANGED
|
@@ -221,6 +221,9 @@ function normalizeFrom(from) {
|
|
|
221
221
|
if (from.address) {
|
|
222
222
|
from.address = from.address.replace(/^[\*\_]+|[\*\_]+$/g, '').trim();
|
|
223
223
|
}
|
|
224
|
+
// FINAL VALIDATION: If at the end we have no address and no name, return null
|
|
225
|
+
if (!from.address && !from.name)
|
|
226
|
+
return null;
|
|
224
227
|
return from;
|
|
225
228
|
}
|
|
226
229
|
function normalizeParserResult(parsed, method, depth, warnings = []) {
|
|
@@ -230,7 +233,6 @@ function normalizeParserResult(parsed, method, depth, warnings = []) {
|
|
|
230
233
|
// Normalize From
|
|
231
234
|
let from = null;
|
|
232
235
|
if (email.from && typeof email.from === 'object') {
|
|
233
|
-
// Only set from if we have at least an address
|
|
234
236
|
if (email.from.address) {
|
|
235
237
|
from = { name: email.from.name, address: email.from.address };
|
|
236
238
|
}
|
|
@@ -238,6 +240,22 @@ function normalizeParserResult(parsed, method, depth, warnings = []) {
|
|
|
238
240
|
else if (typeof email.from === 'string' && email.from.trim()) {
|
|
239
241
|
from = { address: email.from.trim() };
|
|
240
242
|
}
|
|
243
|
+
// Normalize To
|
|
244
|
+
let to_addr = null;
|
|
245
|
+
if (email.to && typeof email.to === 'object') {
|
|
246
|
+
if (Array.isArray(email.to)) {
|
|
247
|
+
if (email.to.length > 0) {
|
|
248
|
+
const first = email.to[0];
|
|
249
|
+
to_addr = typeof first === 'string' ? { address: first } : { name: first.name, address: first.address };
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
else {
|
|
253
|
+
to_addr = { name: email.to.name, address: email.to.address };
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
else if (typeof email.to === 'string' && email.to.trim()) {
|
|
257
|
+
to_addr = { address: email.to.trim() };
|
|
258
|
+
}
|
|
241
259
|
const date_raw = email.date || null;
|
|
242
260
|
const date_iso = normalizeDateToISO(date_raw);
|
|
243
261
|
if (!date_iso && date_raw) {
|
|
@@ -245,6 +263,7 @@ function normalizeParserResult(parsed, method, depth, warnings = []) {
|
|
|
245
263
|
}
|
|
246
264
|
return {
|
|
247
265
|
from,
|
|
266
|
+
to: to_addr,
|
|
248
267
|
subject: email.subject || null,
|
|
249
268
|
date_raw,
|
|
250
269
|
date_iso,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "email-origin-chain",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.10",
|
|
4
4
|
"description": "Uncover the full audit trail of your email threads. Recursively reconstructs the entire conversation history with instant access to the original sender and true source message.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|