@postalsys/email-content 0.0.1
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/LICENSE +21 -0
- package/README.md +288 -0
- package/dist/.tsbuildinfo +1 -0
- package/dist/analyze.d.ts +91 -0
- package/dist/analyze.d.ts.map +1 -0
- package/dist/analyze.js +274 -0
- package/dist/analyze.js.map +1 -0
- package/dist/annotate/client.d.ts +64 -0
- package/dist/annotate/client.d.ts.map +1 -0
- package/dist/annotate/client.js +139 -0
- package/dist/annotate/client.js.map +1 -0
- package/dist/annotate/llm.d.ts +46 -0
- package/dist/annotate/llm.d.ts.map +1 -0
- package/dist/annotate/llm.js +107 -0
- package/dist/annotate/llm.js.map +1 -0
- package/dist/annotate/prompt.d.ts +38 -0
- package/dist/annotate/prompt.d.ts.map +1 -0
- package/dist/annotate/prompt.js +165 -0
- package/dist/annotate/prompt.js.map +1 -0
- package/dist/annotate/store.d.ts +43 -0
- package/dist/annotate/store.d.ts.map +1 -0
- package/dist/annotate/store.js +46 -0
- package/dist/annotate/store.js.map +1 -0
- package/dist/canonical/html.d.ts +33 -0
- package/dist/canonical/html.d.ts.map +1 -0
- package/dist/canonical/html.js +156 -0
- package/dist/canonical/html.js.map +1 -0
- package/dist/canonical/text.d.ts +75 -0
- package/dist/canonical/text.d.ts.map +1 -0
- package/dist/canonical/text.js +217 -0
- package/dist/canonical/text.js.map +1 -0
- package/dist/classify/confidence.d.ts +38 -0
- package/dist/classify/confidence.d.ts.map +1 -0
- package/dist/classify/confidence.js +59 -0
- package/dist/classify/confidence.js.map +1 -0
- package/dist/classify/reconcile.d.ts +52 -0
- package/dist/classify/reconcile.d.ts.map +1 -0
- package/dist/classify/reconcile.js +100 -0
- package/dist/classify/reconcile.js.map +1 -0
- package/dist/classify/rules.d.ts +51 -0
- package/dist/classify/rules.d.ts.map +1 -0
- package/dist/classify/rules.js +648 -0
- package/dist/classify/rules.js.map +1 -0
- package/dist/cli/annotate.d.ts +23 -0
- package/dist/cli/annotate.d.ts.map +1 -0
- package/dist/cli/annotate.js +328 -0
- package/dist/cli/annotate.js.map +1 -0
- package/dist/cli/args.d.ts +17 -0
- package/dist/cli/args.d.ts.map +1 -0
- package/dist/cli/args.js +72 -0
- package/dist/cli/args.js.map +1 -0
- package/dist/cli/benchmark.d.ts +13 -0
- package/dist/cli/benchmark.d.ts.map +1 -0
- package/dist/cli/benchmark.js +172 -0
- package/dist/cli/benchmark.js.map +1 -0
- package/dist/cli/corpus.d.ts +9 -0
- package/dist/cli/corpus.d.ts.map +1 -0
- package/dist/cli/corpus.js +152 -0
- package/dist/cli/corpus.js.map +1 -0
- package/dist/cli/disagreements.d.ts +14 -0
- package/dist/cli/disagreements.d.ts.map +1 -0
- package/dist/cli/disagreements.js +133 -0
- package/dist/cli/disagreements.js.map +1 -0
- package/dist/cli/evaluate.d.ts +15 -0
- package/dist/cli/evaluate.d.ts.map +1 -0
- package/dist/cli/evaluate.js +215 -0
- package/dist/cli/evaluate.js.map +1 -0
- package/dist/cli/extract.d.ts +10 -0
- package/dist/cli/extract.d.ts.map +1 -0
- package/dist/cli/extract.js +139 -0
- package/dist/cli/extract.js.map +1 -0
- package/dist/cli/main.d.ts +9 -0
- package/dist/cli/main.d.ts.map +1 -0
- package/dist/cli/main.js +147 -0
- package/dist/cli/main.js.map +1 -0
- package/dist/corpus/analyze.d.ts +66 -0
- package/dist/corpus/analyze.d.ts.map +1 -0
- package/dist/corpus/analyze.js +214 -0
- package/dist/corpus/analyze.js.map +1 -0
- package/dist/corpus/migrations/index.d.ts +14 -0
- package/dist/corpus/migrations/index.d.ts.map +1 -0
- package/dist/corpus/migrations/index.js +158 -0
- package/dist/corpus/migrations/index.js.map +1 -0
- package/dist/corpus/sample.d.ts +66 -0
- package/dist/corpus/sample.d.ts.map +1 -0
- package/dist/corpus/sample.js +98 -0
- package/dist/corpus/sample.js.map +1 -0
- package/dist/corpus/scanner.d.ts +79 -0
- package/dist/corpus/scanner.d.ts.map +1 -0
- package/dist/corpus/scanner.js +256 -0
- package/dist/corpus/scanner.js.map +1 -0
- package/dist/corpus/stats.d.ts +76 -0
- package/dist/corpus/stats.d.ts.map +1 -0
- package/dist/corpus/stats.js +240 -0
- package/dist/corpus/stats.js.map +1 -0
- package/dist/corpus/store.d.ts +67 -0
- package/dist/corpus/store.d.ts.map +1 -0
- package/dist/corpus/store.js +196 -0
- package/dist/corpus/store.js.map +1 -0
- package/dist/corpus/thread-index.d.ts +31 -0
- package/dist/corpus/thread-index.d.ts.map +1 -0
- package/dist/corpus/thread-index.js +182 -0
- package/dist/corpus/thread-index.js.map +1 -0
- package/dist/corpus/worker.d.ts +21 -0
- package/dist/corpus/worker.d.ts.map +1 -0
- package/dist/corpus/worker.js +74 -0
- package/dist/corpus/worker.js.map +1 -0
- package/dist/detect/client.d.ts +28 -0
- package/dist/detect/client.d.ts.map +1 -0
- package/dist/detect/client.js +121 -0
- package/dist/detect/client.js.map +1 -0
- package/dist/detect/disclaimer.d.ts +12 -0
- package/dist/detect/disclaimer.d.ts.map +1 -0
- package/dist/detect/disclaimer.js +28 -0
- package/dist/detect/disclaimer.js.map +1 -0
- package/dist/detect/forward-header.d.ts +10 -0
- package/dist/detect/forward-header.d.ts.map +1 -0
- package/dist/detect/forward-header.js +35 -0
- package/dist/detect/forward-header.js.map +1 -0
- package/dist/detect/patterns/disclaimers.json +113 -0
- package/dist/detect/patterns/forward-headers.json +40 -0
- package/dist/detect/patterns/html-clients.json +157 -0
- package/dist/detect/patterns/quoted-headers.json +41 -0
- package/dist/detect/patterns/reply-headers.json +139 -0
- package/dist/detect/patterns/signatures.json +58 -0
- package/dist/detect/patterns.d.ts +43 -0
- package/dist/detect/patterns.d.ts.map +1 -0
- package/dist/detect/patterns.js +125 -0
- package/dist/detect/patterns.js.map +1 -0
- package/dist/detect/reply-header.d.ts +44 -0
- package/dist/detect/reply-header.d.ts.map +1 -0
- package/dist/detect/reply-header.js +134 -0
- package/dist/detect/reply-header.js.map +1 -0
- package/dist/detect/signature.d.ts +28 -0
- package/dist/detect/signature.d.ts.map +1 -0
- package/dist/detect/signature.js +77 -0
- package/dist/detect/signature.js.map +1 -0
- package/dist/detect/traffic.d.ts +26 -0
- package/dist/detect/traffic.d.ts.map +1 -0
- package/dist/detect/traffic.js +98 -0
- package/dist/detect/traffic.js.map +1 -0
- package/dist/eval/metrics.d.ts +87 -0
- package/dist/eval/metrics.d.ts.map +1 -0
- package/dist/eval/metrics.js +185 -0
- package/dist/eval/metrics.js.map +1 -0
- package/dist/index.d.ts +54 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +163 -0
- package/dist/index.js.map +1 -0
- package/dist/match/history.d.ts +62 -0
- package/dist/match/history.d.ts.map +1 -0
- package/dist/match/history.js +128 -0
- package/dist/match/history.js.map +1 -0
- package/dist/match/normalize.d.ts +33 -0
- package/dist/match/normalize.d.ts.map +1 -0
- package/dist/match/normalize.js +63 -0
- package/dist/match/normalize.js.map +1 -0
- package/dist/match/shingle.d.ts +44 -0
- package/dist/match/shingle.d.ts.map +1 -0
- package/dist/match/shingle.js +78 -0
- package/dist/match/shingle.js.map +1 -0
- package/dist/mime/charset.d.ts +33 -0
- package/dist/mime/charset.d.ts.map +1 -0
- package/dist/mime/charset.js +118 -0
- package/dist/mime/charset.js.map +1 -0
- package/dist/mime/headers.d.ts +32 -0
- package/dist/mime/headers.d.ts.map +1 -0
- package/dist/mime/headers.js +127 -0
- package/dist/mime/headers.js.map +1 -0
- package/dist/mime/model.d.ts +111 -0
- package/dist/mime/model.d.ts.map +1 -0
- package/dist/mime/model.js +17 -0
- package/dist/mime/model.js.map +1 -0
- package/dist/mime/parse.d.ts +27 -0
- package/dist/mime/parse.d.ts.map +1 -0
- package/dist/mime/parse.js +343 -0
- package/dist/mime/parse.js.map +1 -0
- package/dist/segment/features.d.ts +29 -0
- package/dist/segment/features.d.ts.map +1 -0
- package/dist/segment/features.js +102 -0
- package/dist/segment/features.js.map +1 -0
- package/dist/segment/html-blocks.d.ts +28 -0
- package/dist/segment/html-blocks.d.ts.map +1 -0
- package/dist/segment/html-blocks.js +596 -0
- package/dist/segment/html-blocks.js.map +1 -0
- package/dist/segment/text-blocks.d.ts +30 -0
- package/dist/segment/text-blocks.d.ts.map +1 -0
- package/dist/segment/text-blocks.js +204 -0
- package/dist/segment/text-blocks.js.map +1 -0
- package/dist/segment/types.d.ts +88 -0
- package/dist/segment/types.d.ts.map +1 -0
- package/dist/segment/types.js +11 -0
- package/dist/segment/types.js.map +1 -0
- package/dist/types.d.ts +85 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +17 -0
- package/dist/types.js.map +1 -0
- package/dist/util/hash.d.ts +49 -0
- package/dist/util/hash.d.ts.map +1 -0
- package/dist/util/hash.js +0 -0
- package/dist/util/hash.js.map +1 -0
- package/dist/util/lang.d.ts +25 -0
- package/dist/util/lang.d.ts.map +1 -0
- package/dist/util/lang.js +135 -0
- package/dist/util/lang.js.map +1 -0
- package/dist/util/worker-pool.d.ts +51 -0
- package/dist/util/worker-pool.d.ts.map +1 -0
- package/dist/util/worker-pool.js +155 -0
- package/dist/util/worker-pool.js.map +1 -0
- package/package.json +74 -0
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Language estimation.
|
|
4
|
+
*
|
|
5
|
+
* Deliberately small and dependency free. This is an estimate used for stratified sampling and for
|
|
6
|
+
* reporting metrics per language, never for a classification decision. A wrong guess costs a
|
|
7
|
+
* slightly skewed sample, nothing more, which is why a stopword scorer is preferred over pulling in
|
|
8
|
+
* a model.
|
|
9
|
+
*
|
|
10
|
+
* Two stages: script detection first, because a Cyrillic or CJK body is unambiguous and cheap to
|
|
11
|
+
* spot, then stopword frequency for Latin script languages.
|
|
12
|
+
*/
|
|
13
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
14
|
+
exports.estimateLanguage = estimateLanguage;
|
|
15
|
+
/**
|
|
16
|
+
* Stopword lists. Kept short on purpose: the most frequent function words carry nearly all of the
|
|
17
|
+
* discriminative power, and long lists mostly add overlap between related languages.
|
|
18
|
+
*/
|
|
19
|
+
const STOPWORDS = {
|
|
20
|
+
en: [
|
|
21
|
+
'the',
|
|
22
|
+
'and',
|
|
23
|
+
'you',
|
|
24
|
+
'that',
|
|
25
|
+
'for',
|
|
26
|
+
'with',
|
|
27
|
+
'this',
|
|
28
|
+
'have',
|
|
29
|
+
'not',
|
|
30
|
+
'are',
|
|
31
|
+
'was',
|
|
32
|
+
'but',
|
|
33
|
+
'from',
|
|
34
|
+
'they',
|
|
35
|
+
'will',
|
|
36
|
+
'would',
|
|
37
|
+
'there',
|
|
38
|
+
'their',
|
|
39
|
+
'what',
|
|
40
|
+
'about'
|
|
41
|
+
],
|
|
42
|
+
et: ['ja', 'on', 'ei', 'et', 'see', 'ka', 'kui', 'aga', 'mis', 'oma', 'ning', 'siis', 'ole', 'seda', 'kas', 'nii', 'veel', 'ta', 'ma', 'või'],
|
|
43
|
+
de: ['der', 'die', 'und', 'das', 'ist', 'nicht', 'ich', 'sie', 'mit', 'den', 'von', 'für', 'auf', 'wir', 'auch', 'dem', 'ein', 'eine', 'aber', 'noch'],
|
|
44
|
+
fr: ['les', 'des', 'est', 'une', 'que', 'pour', 'dans', 'pas', 'vous', 'sur', 'avec', 'nous', 'plus', 'par', 'mais', 'ils', 'ce', 'au', 'du', 'je'],
|
|
45
|
+
es: ['que', 'los', 'las', 'del', 'por', 'con', 'para', 'una', 'como', 'más', 'pero', 'sus', 'este', 'esta', 'son', 'muy', 'ser', 'sin', 'sobre', 'todo'],
|
|
46
|
+
it: ['che', 'per', 'con', 'del', 'non', 'una', 'sono', 'della', 'come', 'più', 'anche', 'nel', 'alla', 'ma', 'si', 'da', 'gli', 'questo', 'essere', 'ho'],
|
|
47
|
+
pt: [
|
|
48
|
+
'que',
|
|
49
|
+
'não',
|
|
50
|
+
'com',
|
|
51
|
+
'para',
|
|
52
|
+
'uma',
|
|
53
|
+
'dos',
|
|
54
|
+
'das',
|
|
55
|
+
'por',
|
|
56
|
+
'mais',
|
|
57
|
+
'como',
|
|
58
|
+
'mas',
|
|
59
|
+
'foi',
|
|
60
|
+
'ele',
|
|
61
|
+
'ser',
|
|
62
|
+
'você',
|
|
63
|
+
'está',
|
|
64
|
+
'pelo',
|
|
65
|
+
'isso',
|
|
66
|
+
'quando',
|
|
67
|
+
'muito'
|
|
68
|
+
],
|
|
69
|
+
nl: ['het', 'een', 'van', 'de', 'is', 'dat', 'niet', 'en', 'op', 'te', 'zijn', 'voor', 'met', 'maar', 'ook', 'aan', 'er', 'dan', 'wij', 'heeft'],
|
|
70
|
+
sv: ['och', 'att', 'det', 'som', 'en', 'är', 'på', 'för', 'med', 'den', 'har', 'inte', 'om', 'jag', 'men', 'de', 'till', 'kan', 'vi', 'ett'],
|
|
71
|
+
fi: ['on', 'ja', 'ei', 'se', 'että', 'ovat', 'ole', 'kun', 'niin', 'mutta', 'voi', 'tai', 'jos', 'sen', 'me', 'te', 'hän', 'kuin', 'vain', 'myös'],
|
|
72
|
+
pl: ['nie', 'się', 'jest', 'że', 'do', 'na', 'to', 'jak', 'ale', 'dla', 'przez', 'tego', 'lub', 'oraz', 'tym', 'przy', 'być', 'ma', 'już', 'tylko']
|
|
73
|
+
};
|
|
74
|
+
const SCRIPT_RANGES = [
|
|
75
|
+
{ lang: 'ru', regex: /[Ѐ-ӿ]/g },
|
|
76
|
+
{ lang: 'el', regex: /[Ͱ-Ͽ]/g },
|
|
77
|
+
{ lang: 'he', regex: /[-]/g },
|
|
78
|
+
{ lang: 'ar', regex: /[-ۿ]/g },
|
|
79
|
+
{ lang: 'ko', regex: /[가-ᄀ-ᇿ]/g },
|
|
80
|
+
{ lang: 'ja', regex: /[-ヿ]/g },
|
|
81
|
+
{ lang: 'zh', regex: /[一-鿿]/g }
|
|
82
|
+
];
|
|
83
|
+
/** Minimum tokens needed before a Latin script guess is trusted. */
|
|
84
|
+
const MIN_TOKENS = 12;
|
|
85
|
+
/** Minimum matched share before a guess is returned rather than "unknown". */
|
|
86
|
+
const MIN_SCORE = 0.06;
|
|
87
|
+
/**
|
|
88
|
+
* Estimate the language of a body of text.
|
|
89
|
+
*
|
|
90
|
+
* @param text plain text. Only the first 20k characters are inspected, which is ample and keeps the
|
|
91
|
+
* cost bounded on newsletters and digests.
|
|
92
|
+
*/
|
|
93
|
+
function estimateLanguage(text) {
|
|
94
|
+
const sample = text.length > 20000 ? text.slice(0, 20000) : text;
|
|
95
|
+
if (!sample.trim()) {
|
|
96
|
+
return { lang: 'unknown', score: 0 };
|
|
97
|
+
}
|
|
98
|
+
// Script detection first. A body with a meaningful share of non Latin letters is decided here.
|
|
99
|
+
const letters = sample.replace(/[^\p{L}]/gu, '').length;
|
|
100
|
+
if (letters > 20) {
|
|
101
|
+
for (const { lang, regex } of SCRIPT_RANGES) {
|
|
102
|
+
regex.lastIndex = 0;
|
|
103
|
+
const matches = sample.match(regex);
|
|
104
|
+
if (matches && matches.length / letters > 0.2) {
|
|
105
|
+
return { lang, score: matches.length / letters };
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
const tokens = sample
|
|
110
|
+
.toLowerCase()
|
|
111
|
+
.split(/[^\p{L}']+/u)
|
|
112
|
+
.filter(token => token.length > 1);
|
|
113
|
+
if (tokens.length < MIN_TOKENS) {
|
|
114
|
+
return { lang: 'unknown', score: 0 };
|
|
115
|
+
}
|
|
116
|
+
const counts = new Map();
|
|
117
|
+
for (const token of tokens) {
|
|
118
|
+
counts.set(token, (counts.get(token) || 0) + 1);
|
|
119
|
+
}
|
|
120
|
+
let bestLang = 'unknown';
|
|
121
|
+
let bestScore = 0;
|
|
122
|
+
for (const [lang, words] of Object.entries(STOPWORDS)) {
|
|
123
|
+
let hits = 0;
|
|
124
|
+
for (const word of words) {
|
|
125
|
+
hits += counts.get(word) || 0;
|
|
126
|
+
}
|
|
127
|
+
const score = hits / tokens.length;
|
|
128
|
+
if (score > bestScore) {
|
|
129
|
+
bestScore = score;
|
|
130
|
+
bestLang = lang;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
return bestScore >= MIN_SCORE ? { lang: bestLang, score: bestScore } : { lang: 'unknown', score: bestScore };
|
|
134
|
+
}
|
|
135
|
+
//# sourceMappingURL=lang.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lang.js","sourceRoot":"","sources":["../../src/util/lang.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;GAUG;;AA6FH,4CA8CC;AAhID;;;GAGG;AACH,MAAM,SAAS,GAAsC;IACjD,EAAE,EAAE;QACA,KAAK;QACL,KAAK;QACL,KAAK;QACL,MAAM;QACN,KAAK;QACL,MAAM;QACN,MAAM;QACN,MAAM;QACN,KAAK;QACL,KAAK;QACL,KAAK;QACL,KAAK;QACL,MAAM;QACN,MAAM;QACN,MAAM;QACN,OAAO;QACP,OAAO;QACP,OAAO;QACP,MAAM;QACN,OAAO;KACV;IACD,EAAE,EAAE,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,CAAC;IAC7I,EAAE,EAAE,CAAC,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC;IACtJ,EAAE,EAAE,CAAC,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC;IACnJ,EAAE,EAAE,CAAC,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,CAAC;IACxJ,EAAE,EAAE,CAAC,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,IAAI,CAAC;IACzJ,EAAE,EAAE;QACA,KAAK;QACL,KAAK;QACL,KAAK;QACL,MAAM;QACN,KAAK;QACL,KAAK;QACL,KAAK;QACL,KAAK;QACL,MAAM;QACN,MAAM;QACN,KAAK;QACL,KAAK;QACL,KAAK;QACL,KAAK;QACL,MAAM;QACN,MAAM;QACN,MAAM;QACN,MAAM;QACN,QAAQ;QACR,OAAO;KACV;IACD,EAAE,EAAE,CAAC,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,OAAO,CAAC;IAChJ,EAAE,EAAE,CAAC,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,CAAC;IAC5I,EAAE,EAAE,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC;IAClJ,EAAE,EAAE,CAAC,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,OAAO,CAAC;CACtJ,CAAC;AAEF,MAAM,aAAa,GAAqD;IACpE,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,QAAQ,EAAE;IAC/B,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,QAAQ,EAAE;IAC/B,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,QAAQ,EAAE;IAC/B,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,QAAQ,EAAE;IAC/B,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,WAAW,EAAE;IAClC,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,QAAQ,EAAE;IAC/B,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,QAAQ,EAAE;CAClC,CAAC;AAEF,oEAAoE;AACpE,MAAM,UAAU,GAAG,EAAE,CAAC;AAEtB,8EAA8E;AAC9E,MAAM,SAAS,GAAG,IAAI,CAAC;AAEvB;;;;;GAKG;AACH,SAAgB,gBAAgB,CAAC,IAAY;IACzC,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,GAAG,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;IACjE,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE,EAAE,CAAC;QACjB,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC;IACzC,CAAC;IAED,+FAA+F;IAC/F,MAAM,OAAO,GAAG,MAAM,CAAC,OAAO,CAAC,YAAY,EAAE,EAAE,CAAC,CAAC,MAAM,CAAC;IACxD,IAAI,OAAO,GAAG,EAAE,EAAE,CAAC;QACf,KAAK,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,aAAa,EAAE,CAAC;YAC1C,KAAK,CAAC,SAAS,GAAG,CAAC,CAAC;YACpB,MAAM,OAAO,GAAG,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;YACpC,IAAI,OAAO,IAAI,OAAO,CAAC,MAAM,GAAG,OAAO,GAAG,GAAG,EAAE,CAAC;gBAC5C,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,OAAO,CAAC,MAAM,GAAG,OAAO,EAAE,CAAC;YACrD,CAAC;QACL,CAAC;IACL,CAAC;IAED,MAAM,MAAM,GAAG,MAAM;SAChB,WAAW,EAAE;SACb,KAAK,CAAC,aAAa,CAAC;SACpB,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;IACvC,IAAI,MAAM,CAAC,MAAM,GAAG,UAAU,EAAE,CAAC;QAC7B,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC;IACzC,CAAC;IAED,MAAM,MAAM,GAAG,IAAI,GAAG,EAAkB,CAAC;IACzC,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;QACzB,MAAM,CAAC,GAAG,CAAC,KAAK,EAAE,CAAC,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;IACpD,CAAC;IAED,IAAI,QAAQ,GAAiB,SAAS,CAAC;IACvC,IAAI,SAAS,GAAG,CAAC,CAAC;IAClB,KAAK,MAAM,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,EAAE,CAAC;QACpD,IAAI,IAAI,GAAG,CAAC,CAAC;QACb,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;YACvB,IAAI,IAAI,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAClC,CAAC;QACD,MAAM,KAAK,GAAG,IAAI,GAAG,MAAM,CAAC,MAAM,CAAC;QACnC,IAAI,KAAK,GAAG,SAAS,EAAE,CAAC;YACpB,SAAS,GAAG,KAAK,CAAC;YAClB,QAAQ,GAAG,IAAoB,CAAC;QACpC,CAAC;IACL,CAAC;IAED,OAAO,SAAS,IAAI,SAAS,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,SAAS,EAAE,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,SAAS,EAAE,CAAC;AACjH,CAAC"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fixed size worker pool with per task timeouts.
|
|
3
|
+
*
|
|
4
|
+
* Adapted from the pool in @postalsys/email-text-tools (lib/worker-pool.js), simplified for batch
|
|
5
|
+
* work: the corpus scanner knows how many workers it wants and drives its own backpressure, so the
|
|
6
|
+
* elastic sizing and idle reaping of the original are not needed. What is kept is the important
|
|
7
|
+
* part, a task timeout that terminates and replaces the worker, so that one pathological message
|
|
8
|
+
* cannot wedge a scan of 320k files.
|
|
9
|
+
*/
|
|
10
|
+
import { EventEmitter } from 'node:events';
|
|
11
|
+
export interface WorkerPoolOptions {
|
|
12
|
+
workerPath: string;
|
|
13
|
+
size: number;
|
|
14
|
+
/** Per task timeout in milliseconds. */
|
|
15
|
+
taskTimeoutMs: number;
|
|
16
|
+
/** Optional data passed to every worker at startup. */
|
|
17
|
+
workerData?: unknown;
|
|
18
|
+
/**
|
|
19
|
+
* Old generation heap cap per worker, in MiB.
|
|
20
|
+
*
|
|
21
|
+
* Worker threads share the parent process, so their heaps all count towards its RSS. Without a
|
|
22
|
+
* cap, sixteen workers each growing to a comfortable default is more than a gigabyte of
|
|
23
|
+
* resident memory for a job whose working set is one message at a time. Exceeding the cap
|
|
24
|
+
* terminates that worker, which the pool treats like any other worker failure.
|
|
25
|
+
*/
|
|
26
|
+
maxOldGenerationSizeMb?: number;
|
|
27
|
+
}
|
|
28
|
+
export declare class WorkerTimeoutError extends Error {
|
|
29
|
+
constructor(ms: number);
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Emits 'worker-error' with the underlying error whenever a worker dies unexpectedly. Callers may
|
|
33
|
+
* log it; the pool itself recovers by spawning a replacement.
|
|
34
|
+
*/
|
|
35
|
+
export declare class WorkerPool<TTask, TResult> extends EventEmitter {
|
|
36
|
+
private readonly options;
|
|
37
|
+
private readonly slots;
|
|
38
|
+
private readonly queue;
|
|
39
|
+
private nextId;
|
|
40
|
+
private closing;
|
|
41
|
+
constructor(options: WorkerPoolOptions);
|
|
42
|
+
private spawn;
|
|
43
|
+
private replace;
|
|
44
|
+
private dispatch;
|
|
45
|
+
private drainQueue;
|
|
46
|
+
/** Number of tasks accepted but not yet completed. */
|
|
47
|
+
get inFlight(): number;
|
|
48
|
+
submit(task: TTask): Promise<TResult>;
|
|
49
|
+
close(): Promise<void>;
|
|
50
|
+
}
|
|
51
|
+
//# sourceMappingURL=worker-pool.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"worker-pool.d.ts","sourceRoot":"","sources":["../../src/util/worker-pool.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAGH,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAE3C,MAAM,WAAW,iBAAiB;IAC9B,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,wCAAwC;IACxC,aAAa,EAAE,MAAM,CAAC;IACtB,uDAAuD;IACvD,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB;;;;;;;OAOG;IACH,sBAAsB,CAAC,EAAE,MAAM,CAAC;CACnC;AA+BD,qBAAa,kBAAmB,SAAQ,KAAK;gBAC7B,EAAE,EAAE,MAAM;CAIzB;AAED;;;GAGG;AACH,qBAAa,UAAU,CAAC,KAAK,EAAE,OAAO,CAAE,SAAQ,YAAY;IACxD,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAoB;IAC5C,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAuB;IAC7C,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAgC;IACtD,OAAO,CAAC,MAAM,CAAK;IACnB,OAAO,CAAC,OAAO,CAAS;gBAEZ,OAAO,EAAE,iBAAiB;IAQtC,OAAO,CAAC,KAAK;IA4Cb,OAAO,CAAC,OAAO;IAaf,OAAO,CAAC,QAAQ;IAkBhB,OAAO,CAAC,UAAU;IAclB,sDAAsD;IACtD,IAAI,QAAQ,IAAI,MAAM,CAErB;IAED,MAAM,CAAC,IAAI,EAAE,KAAK,GAAG,OAAO,CAAC,OAAO,CAAC;IAe/B,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;CAgB/B"}
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Fixed size worker pool with per task timeouts.
|
|
4
|
+
*
|
|
5
|
+
* Adapted from the pool in @postalsys/email-text-tools (lib/worker-pool.js), simplified for batch
|
|
6
|
+
* work: the corpus scanner knows how many workers it wants and drives its own backpressure, so the
|
|
7
|
+
* elastic sizing and idle reaping of the original are not needed. What is kept is the important
|
|
8
|
+
* part, a task timeout that terminates and replaces the worker, so that one pathological message
|
|
9
|
+
* cannot wedge a scan of 320k files.
|
|
10
|
+
*/
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.WorkerPool = exports.WorkerTimeoutError = void 0;
|
|
13
|
+
const node_worker_threads_1 = require("node:worker_threads");
|
|
14
|
+
const node_events_1 = require("node:events");
|
|
15
|
+
class WorkerTimeoutError extends Error {
|
|
16
|
+
constructor(ms) {
|
|
17
|
+
super(`worker task timed out after ${ms}ms`);
|
|
18
|
+
this.name = 'WorkerTimeoutError';
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
exports.WorkerTimeoutError = WorkerTimeoutError;
|
|
22
|
+
/**
|
|
23
|
+
* Emits 'worker-error' with the underlying error whenever a worker dies unexpectedly. Callers may
|
|
24
|
+
* log it; the pool itself recovers by spawning a replacement.
|
|
25
|
+
*/
|
|
26
|
+
class WorkerPool extends node_events_1.EventEmitter {
|
|
27
|
+
options;
|
|
28
|
+
slots = [];
|
|
29
|
+
queue = [];
|
|
30
|
+
nextId = 1;
|
|
31
|
+
closing = false;
|
|
32
|
+
constructor(options) {
|
|
33
|
+
super();
|
|
34
|
+
this.options = options;
|
|
35
|
+
for (let i = 0; i < Math.max(1, options.size); i++) {
|
|
36
|
+
this.spawn();
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
spawn() {
|
|
40
|
+
const worker = new node_worker_threads_1.Worker(this.options.workerPath, {
|
|
41
|
+
workerData: this.options.workerData,
|
|
42
|
+
resourceLimits: this.options.maxOldGenerationSizeMb ? { maxOldGenerationSizeMb: this.options.maxOldGenerationSizeMb } : undefined
|
|
43
|
+
});
|
|
44
|
+
const slot = { worker, busy: false, current: null };
|
|
45
|
+
worker.on('message', (reply) => {
|
|
46
|
+
const current = slot.current;
|
|
47
|
+
if (!current || current.id !== reply.id) {
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
clearTimeout(current.timer);
|
|
51
|
+
slot.current = null;
|
|
52
|
+
slot.busy = false;
|
|
53
|
+
if (reply.ok) {
|
|
54
|
+
current.resolve(reply.result);
|
|
55
|
+
}
|
|
56
|
+
else {
|
|
57
|
+
current.reject(new Error(reply.error || 'worker failed'));
|
|
58
|
+
}
|
|
59
|
+
this.drainQueue();
|
|
60
|
+
});
|
|
61
|
+
worker.on('error', err => {
|
|
62
|
+
this.emit('worker-error', err);
|
|
63
|
+
const current = slot.current;
|
|
64
|
+
if (current) {
|
|
65
|
+
clearTimeout(current.timer);
|
|
66
|
+
slot.current = null;
|
|
67
|
+
current.reject(err instanceof Error ? err : new Error(String(err)));
|
|
68
|
+
}
|
|
69
|
+
this.replace(slot);
|
|
70
|
+
});
|
|
71
|
+
worker.on('exit', () => {
|
|
72
|
+
if (!this.closing && this.slots.includes(slot)) {
|
|
73
|
+
this.replace(slot);
|
|
74
|
+
}
|
|
75
|
+
});
|
|
76
|
+
this.slots.push(slot);
|
|
77
|
+
return slot;
|
|
78
|
+
}
|
|
79
|
+
replace(slot) {
|
|
80
|
+
const index = this.slots.indexOf(slot);
|
|
81
|
+
if (index !== -1) {
|
|
82
|
+
this.slots.splice(index, 1);
|
|
83
|
+
}
|
|
84
|
+
slot.busy = false;
|
|
85
|
+
void slot.worker.terminate().catch(() => undefined);
|
|
86
|
+
if (!this.closing) {
|
|
87
|
+
this.spawn();
|
|
88
|
+
this.drainQueue();
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
dispatch(slot, item) {
|
|
92
|
+
slot.busy = true;
|
|
93
|
+
const timer = setTimeout(() => {
|
|
94
|
+
const current = slot.current;
|
|
95
|
+
slot.current = null;
|
|
96
|
+
if (current) {
|
|
97
|
+
current.reject(new WorkerTimeoutError(this.options.taskTimeoutMs));
|
|
98
|
+
}
|
|
99
|
+
// The worker is stuck in whatever wedged it, so it cannot be reused.
|
|
100
|
+
this.replace(slot);
|
|
101
|
+
}, this.options.taskTimeoutMs);
|
|
102
|
+
// A pending timer must not hold the process open on its own.
|
|
103
|
+
timer.unref?.();
|
|
104
|
+
slot.current = { id: item.envelope.id, resolve: item.resolve, reject: item.reject, timer };
|
|
105
|
+
slot.worker.postMessage(item.envelope);
|
|
106
|
+
}
|
|
107
|
+
drainQueue() {
|
|
108
|
+
while (this.queue.length) {
|
|
109
|
+
const slot = this.slots.find(candidate => !candidate.busy);
|
|
110
|
+
if (!slot) {
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
const item = this.queue.shift();
|
|
114
|
+
if (!item) {
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
this.dispatch(slot, item);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
/** Number of tasks accepted but not yet completed. */
|
|
121
|
+
get inFlight() {
|
|
122
|
+
return this.queue.length + this.slots.filter(slot => slot.busy).length;
|
|
123
|
+
}
|
|
124
|
+
submit(task) {
|
|
125
|
+
if (this.closing) {
|
|
126
|
+
return Promise.reject(new Error('worker pool is closing'));
|
|
127
|
+
}
|
|
128
|
+
return new Promise((resolve, reject) => {
|
|
129
|
+
const item = { envelope: { id: this.nextId++, task }, resolve, reject };
|
|
130
|
+
const slot = this.slots.find(candidate => !candidate.busy);
|
|
131
|
+
if (slot) {
|
|
132
|
+
this.dispatch(slot, item);
|
|
133
|
+
}
|
|
134
|
+
else {
|
|
135
|
+
this.queue.push(item);
|
|
136
|
+
}
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
async close() {
|
|
140
|
+
this.closing = true;
|
|
141
|
+
while (this.queue.length) {
|
|
142
|
+
this.queue.shift()?.reject(new Error('worker pool is closing'));
|
|
143
|
+
}
|
|
144
|
+
const slots = [...this.slots];
|
|
145
|
+
this.slots.length = 0;
|
|
146
|
+
await Promise.all(slots.map(slot => {
|
|
147
|
+
if (slot.current) {
|
|
148
|
+
clearTimeout(slot.current.timer);
|
|
149
|
+
}
|
|
150
|
+
return slot.worker.terminate().catch(() => undefined);
|
|
151
|
+
}));
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
exports.WorkerPool = WorkerPool;
|
|
155
|
+
//# sourceMappingURL=worker-pool.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"worker-pool.js","sourceRoot":"","sources":["../../src/util/worker-pool.ts"],"names":[],"mappings":";AAAA;;;;;;;;GAQG;;;AAEH,6DAA6C;AAC7C,6CAA2C;AAiD3C,MAAa,kBAAmB,SAAQ,KAAK;IACzC,YAAY,EAAU;QAClB,KAAK,CAAC,+BAA+B,EAAE,IAAI,CAAC,CAAC;QAC7C,IAAI,CAAC,IAAI,GAAG,oBAAoB,CAAC;IACrC,CAAC;CACJ;AALD,gDAKC;AAED;;;GAGG;AACH,MAAa,UAA2B,SAAQ,0BAAY;IACvC,OAAO,CAAoB;IAC3B,KAAK,GAAoB,EAAE,CAAC;IAC5B,KAAK,GAA6B,EAAE,CAAC;IAC9C,MAAM,GAAG,CAAC,CAAC;IACX,OAAO,GAAG,KAAK,CAAC;IAExB,YAAY,OAA0B;QAClC,KAAK,EAAE,CAAC;QACR,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;QACvB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;YACjD,IAAI,CAAC,KAAK,EAAE,CAAC;QACjB,CAAC;IACL,CAAC;IAEO,KAAK;QACT,MAAM,MAAM,GAAG,IAAI,4BAAM,CAAC,IAAI,CAAC,OAAO,CAAC,UAAU,EAAE;YAC/C,UAAU,EAAE,IAAI,CAAC,OAAO,CAAC,UAAU;YACnC,cAAc,EAAE,IAAI,CAAC,OAAO,CAAC,sBAAsB,CAAC,CAAC,CAAC,EAAE,sBAAsB,EAAE,IAAI,CAAC,OAAO,CAAC,sBAAsB,EAAE,CAAC,CAAC,CAAC,SAAS;SACpI,CAAC,CAAC;QACH,MAAM,IAAI,GAAkB,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;QAEnE,MAAM,CAAC,EAAE,CAAC,SAAS,EAAE,CAAC,KAAqB,EAAE,EAAE;YAC3C,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC;YAC7B,IAAI,CAAC,OAAO,IAAI,OAAO,CAAC,EAAE,KAAK,KAAK,CAAC,EAAE,EAAE,CAAC;gBACtC,OAAO;YACX,CAAC;YACD,YAAY,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;YAC5B,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC;YACpB,IAAI,CAAC,IAAI,GAAG,KAAK,CAAC;YAClB,IAAI,KAAK,CAAC,EAAE,EAAE,CAAC;gBACX,OAAO,CAAC,OAAO,CAAC,KAAK,CAAC,MAAiB,CAAC,CAAC;YAC7C,CAAC;iBAAM,CAAC;gBACJ,OAAO,CAAC,MAAM,CAAC,IAAI,KAAK,CAAC,KAAK,CAAC,KAAK,IAAI,eAAe,CAAC,CAAC,CAAC;YAC9D,CAAC;YACD,IAAI,CAAC,UAAU,EAAE,CAAC;QACtB,CAAC,CAAC,CAAC;QAEH,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,CAAC,EAAE;YACrB,IAAI,CAAC,IAAI,CAAC,cAAc,EAAE,GAAG,CAAC,CAAC;YAC/B,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC;YAC7B,IAAI,OAAO,EAAE,CAAC;gBACV,YAAY,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;gBAC5B,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC;gBACpB,OAAO,CAAC,MAAM,CAAC,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;YACxE,CAAC;YACD,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;QACvB,CAAC,CAAC,CAAC;QAEH,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,GAAG,EAAE;YACnB,IAAI,CAAC,IAAI,CAAC,OAAO,IAAI,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;gBAC7C,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;YACvB,CAAC;QACL,CAAC,CAAC,CAAC;QAEH,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACtB,OAAO,IAAI,CAAC;IAChB,CAAC;IAEO,OAAO,CAAC,IAAmB;QAC/B,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;QACvC,IAAI,KAAK,KAAK,CAAC,CAAC,EAAE,CAAC;YACf,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC;QAChC,CAAC;QACD,IAAI,CAAC,IAAI,GAAG,KAAK,CAAC;QAClB,KAAK,IAAI,CAAC,MAAM,CAAC,SAAS,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAC;QACpD,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC;YAChB,IAAI,CAAC,KAAK,EAAE,CAAC;YACb,IAAI,CAAC,UAAU,EAAE,CAAC;QACtB,CAAC;IACL,CAAC;IAEO,QAAQ,CAAC,IAAmB,EAAE,IAA4B;QAC9D,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACjB,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE;YAC1B,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC;YAC7B,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC;YACpB,IAAI,OAAO,EAAE,CAAC;gBACV,OAAO,CAAC,MAAM,CAAC,IAAI,kBAAkB,CAAC,IAAI,CAAC,OAAO,CAAC,aAAa,CAAC,CAAC,CAAC;YACvE,CAAC;YACD,qEAAqE;YACrE,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;QACvB,CAAC,EAAE,IAAI,CAAC,OAAO,CAAC,aAAa,CAAC,CAAC;QAC/B,6DAA6D;QAC7D,KAAK,CAAC,KAAK,EAAE,EAAE,CAAC;QAEhB,IAAI,CAAC,OAAO,GAAG,EAAE,EAAE,EAAE,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,KAAK,EAAE,CAAC;QAC3F,IAAI,CAAC,MAAM,CAAC,WAAW,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IAC3C,CAAC;IAEO,UAAU;QACd,OAAO,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC;YACvB,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;YAC3D,IAAI,CAAC,IAAI,EAAE,CAAC;gBACR,OAAO;YACX,CAAC;YACD,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC;YAChC,IAAI,CAAC,IAAI,EAAE,CAAC;gBACR,OAAO;YACX,CAAC;YACD,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;QAC9B,CAAC;IACL,CAAC;IAED,sDAAsD;IACtD,IAAI,QAAQ;QACR,OAAO,IAAI,CAAC,KAAK,CAAC,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC;IAC3E,CAAC;IAED,MAAM,CAAC,IAAW;QACd,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;YACf,OAAO,OAAO,CAAC,MAAM,CAAC,IAAI,KAAK,CAAC,wBAAwB,CAAC,CAAC,CAAC;QAC/D,CAAC;QACD,OAAO,IAAI,OAAO,CAAU,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;YAC5C,MAAM,IAAI,GAA2B,EAAE,QAAQ,EAAE,EAAE,EAAE,EAAE,IAAI,CAAC,MAAM,EAAE,EAAE,IAAI,EAAE,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC;YAChG,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;YAC3D,IAAI,IAAI,EAAE,CAAC;gBACP,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;YAC9B,CAAC;iBAAM,CAAC;gBACJ,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAC1B,CAAC;QACL,CAAC,CAAC,CAAC;IACP,CAAC;IAED,KAAK,CAAC,KAAK;QACP,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC;QACpB,OAAO,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC;YACvB,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,EAAE,MAAM,CAAC,IAAI,KAAK,CAAC,wBAAwB,CAAC,CAAC,CAAC;QACpE,CAAC;QACD,MAAM,KAAK,GAAG,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC;QAC9B,IAAI,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC;QACtB,MAAM,OAAO,CAAC,GAAG,CACb,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE;YACb,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;gBACf,YAAY,CAAC,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;YACrC,CAAC;YACD,OAAO,IAAI,CAAC,MAAM,CAAC,SAAS,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAC;QAC1D,CAAC,CAAC,CACL,CAAC;IACN,CAAC;CACJ;AA5ID,gCA4IC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@postalsys/email-content",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "Extract conversationally relevant content from arbitrary email messages",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"email",
|
|
7
|
+
"mime",
|
|
8
|
+
"reply",
|
|
9
|
+
"quote",
|
|
10
|
+
"signature",
|
|
11
|
+
"extraction",
|
|
12
|
+
"parser"
|
|
13
|
+
],
|
|
14
|
+
"license": "MIT",
|
|
15
|
+
"author": "Postal Systems OÜ",
|
|
16
|
+
"repository": {
|
|
17
|
+
"type": "git",
|
|
18
|
+
"url": "git+https://github.com/postalsys/email-content.git"
|
|
19
|
+
},
|
|
20
|
+
"bugs": {
|
|
21
|
+
"url": "https://github.com/postalsys/email-content/issues"
|
|
22
|
+
},
|
|
23
|
+
"homepage": "https://github.com/postalsys/email-content#readme",
|
|
24
|
+
"type": "commonjs",
|
|
25
|
+
"main": "dist/index.js",
|
|
26
|
+
"types": "dist/index.d.ts",
|
|
27
|
+
"exports": {
|
|
28
|
+
".": {
|
|
29
|
+
"types": "./dist/index.d.ts",
|
|
30
|
+
"require": "./dist/index.js",
|
|
31
|
+
"default": "./dist/index.js"
|
|
32
|
+
},
|
|
33
|
+
"./package.json": "./package.json"
|
|
34
|
+
},
|
|
35
|
+
"bin": {
|
|
36
|
+
"email-content": "dist/cli/main.js"
|
|
37
|
+
},
|
|
38
|
+
"files": [
|
|
39
|
+
"dist"
|
|
40
|
+
],
|
|
41
|
+
"engines": {
|
|
42
|
+
"node": ">=24.0.0"
|
|
43
|
+
},
|
|
44
|
+
"publishConfig": {
|
|
45
|
+
"access": "public"
|
|
46
|
+
},
|
|
47
|
+
"scripts": {
|
|
48
|
+
"build": "tsc -p tsconfig.json && node scripts/copy-assets.js",
|
|
49
|
+
"build:test": "tsc -p tsconfig.test.json && node scripts/copy-assets.js && node scripts/copy-assets-test.js",
|
|
50
|
+
"clean": "rm -rf dist .build-test",
|
|
51
|
+
"typecheck": "tsc -p tsconfig.test.json --noEmit",
|
|
52
|
+
"lint": "eslint src test",
|
|
53
|
+
"format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\" \"docs/**/*.md\" \"*.json\" \"*.js\"",
|
|
54
|
+
"format:check": "prettier --check \"src/**/*.ts\" \"test/**/*.ts\" \"docs/**/*.md\" \"*.json\" \"*.js\"",
|
|
55
|
+
"test": "npm run build:test && node --test \".build-test/test/**/*.test.js\"",
|
|
56
|
+
"test:fast": "node --test \"test/**/*.test.ts\"",
|
|
57
|
+
"prepublishOnly": "npm run build",
|
|
58
|
+
"cli": "node dist/cli/main.js"
|
|
59
|
+
},
|
|
60
|
+
"dependencies": {
|
|
61
|
+
"@zone-eu/mailsplit": "5.4.14",
|
|
62
|
+
"libmime": "5.4.1",
|
|
63
|
+
"node-html-parser": "9.0.0"
|
|
64
|
+
},
|
|
65
|
+
"devDependencies": {
|
|
66
|
+
"@eslint/js": "10.0.1",
|
|
67
|
+
"@types/node": "24.13.3",
|
|
68
|
+
"eslint": "10.8.0",
|
|
69
|
+
"eslint-config-prettier": "10.1.8",
|
|
70
|
+
"prettier": "3.9.6",
|
|
71
|
+
"typescript": "5.9.3",
|
|
72
|
+
"typescript-eslint": "8.65.0"
|
|
73
|
+
}
|
|
74
|
+
}
|