@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.
Files changed (210) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +288 -0
  3. package/dist/.tsbuildinfo +1 -0
  4. package/dist/analyze.d.ts +91 -0
  5. package/dist/analyze.d.ts.map +1 -0
  6. package/dist/analyze.js +274 -0
  7. package/dist/analyze.js.map +1 -0
  8. package/dist/annotate/client.d.ts +64 -0
  9. package/dist/annotate/client.d.ts.map +1 -0
  10. package/dist/annotate/client.js +139 -0
  11. package/dist/annotate/client.js.map +1 -0
  12. package/dist/annotate/llm.d.ts +46 -0
  13. package/dist/annotate/llm.d.ts.map +1 -0
  14. package/dist/annotate/llm.js +107 -0
  15. package/dist/annotate/llm.js.map +1 -0
  16. package/dist/annotate/prompt.d.ts +38 -0
  17. package/dist/annotate/prompt.d.ts.map +1 -0
  18. package/dist/annotate/prompt.js +165 -0
  19. package/dist/annotate/prompt.js.map +1 -0
  20. package/dist/annotate/store.d.ts +43 -0
  21. package/dist/annotate/store.d.ts.map +1 -0
  22. package/dist/annotate/store.js +46 -0
  23. package/dist/annotate/store.js.map +1 -0
  24. package/dist/canonical/html.d.ts +33 -0
  25. package/dist/canonical/html.d.ts.map +1 -0
  26. package/dist/canonical/html.js +156 -0
  27. package/dist/canonical/html.js.map +1 -0
  28. package/dist/canonical/text.d.ts +75 -0
  29. package/dist/canonical/text.d.ts.map +1 -0
  30. package/dist/canonical/text.js +217 -0
  31. package/dist/canonical/text.js.map +1 -0
  32. package/dist/classify/confidence.d.ts +38 -0
  33. package/dist/classify/confidence.d.ts.map +1 -0
  34. package/dist/classify/confidence.js +59 -0
  35. package/dist/classify/confidence.js.map +1 -0
  36. package/dist/classify/reconcile.d.ts +52 -0
  37. package/dist/classify/reconcile.d.ts.map +1 -0
  38. package/dist/classify/reconcile.js +100 -0
  39. package/dist/classify/reconcile.js.map +1 -0
  40. package/dist/classify/rules.d.ts +51 -0
  41. package/dist/classify/rules.d.ts.map +1 -0
  42. package/dist/classify/rules.js +648 -0
  43. package/dist/classify/rules.js.map +1 -0
  44. package/dist/cli/annotate.d.ts +23 -0
  45. package/dist/cli/annotate.d.ts.map +1 -0
  46. package/dist/cli/annotate.js +328 -0
  47. package/dist/cli/annotate.js.map +1 -0
  48. package/dist/cli/args.d.ts +17 -0
  49. package/dist/cli/args.d.ts.map +1 -0
  50. package/dist/cli/args.js +72 -0
  51. package/dist/cli/args.js.map +1 -0
  52. package/dist/cli/benchmark.d.ts +13 -0
  53. package/dist/cli/benchmark.d.ts.map +1 -0
  54. package/dist/cli/benchmark.js +172 -0
  55. package/dist/cli/benchmark.js.map +1 -0
  56. package/dist/cli/corpus.d.ts +9 -0
  57. package/dist/cli/corpus.d.ts.map +1 -0
  58. package/dist/cli/corpus.js +152 -0
  59. package/dist/cli/corpus.js.map +1 -0
  60. package/dist/cli/disagreements.d.ts +14 -0
  61. package/dist/cli/disagreements.d.ts.map +1 -0
  62. package/dist/cli/disagreements.js +133 -0
  63. package/dist/cli/disagreements.js.map +1 -0
  64. package/dist/cli/evaluate.d.ts +15 -0
  65. package/dist/cli/evaluate.d.ts.map +1 -0
  66. package/dist/cli/evaluate.js +215 -0
  67. package/dist/cli/evaluate.js.map +1 -0
  68. package/dist/cli/extract.d.ts +10 -0
  69. package/dist/cli/extract.d.ts.map +1 -0
  70. package/dist/cli/extract.js +139 -0
  71. package/dist/cli/extract.js.map +1 -0
  72. package/dist/cli/main.d.ts +9 -0
  73. package/dist/cli/main.d.ts.map +1 -0
  74. package/dist/cli/main.js +147 -0
  75. package/dist/cli/main.js.map +1 -0
  76. package/dist/corpus/analyze.d.ts +66 -0
  77. package/dist/corpus/analyze.d.ts.map +1 -0
  78. package/dist/corpus/analyze.js +214 -0
  79. package/dist/corpus/analyze.js.map +1 -0
  80. package/dist/corpus/migrations/index.d.ts +14 -0
  81. package/dist/corpus/migrations/index.d.ts.map +1 -0
  82. package/dist/corpus/migrations/index.js +158 -0
  83. package/dist/corpus/migrations/index.js.map +1 -0
  84. package/dist/corpus/sample.d.ts +66 -0
  85. package/dist/corpus/sample.d.ts.map +1 -0
  86. package/dist/corpus/sample.js +98 -0
  87. package/dist/corpus/sample.js.map +1 -0
  88. package/dist/corpus/scanner.d.ts +79 -0
  89. package/dist/corpus/scanner.d.ts.map +1 -0
  90. package/dist/corpus/scanner.js +256 -0
  91. package/dist/corpus/scanner.js.map +1 -0
  92. package/dist/corpus/stats.d.ts +76 -0
  93. package/dist/corpus/stats.d.ts.map +1 -0
  94. package/dist/corpus/stats.js +240 -0
  95. package/dist/corpus/stats.js.map +1 -0
  96. package/dist/corpus/store.d.ts +67 -0
  97. package/dist/corpus/store.d.ts.map +1 -0
  98. package/dist/corpus/store.js +196 -0
  99. package/dist/corpus/store.js.map +1 -0
  100. package/dist/corpus/thread-index.d.ts +31 -0
  101. package/dist/corpus/thread-index.d.ts.map +1 -0
  102. package/dist/corpus/thread-index.js +182 -0
  103. package/dist/corpus/thread-index.js.map +1 -0
  104. package/dist/corpus/worker.d.ts +21 -0
  105. package/dist/corpus/worker.d.ts.map +1 -0
  106. package/dist/corpus/worker.js +74 -0
  107. package/dist/corpus/worker.js.map +1 -0
  108. package/dist/detect/client.d.ts +28 -0
  109. package/dist/detect/client.d.ts.map +1 -0
  110. package/dist/detect/client.js +121 -0
  111. package/dist/detect/client.js.map +1 -0
  112. package/dist/detect/disclaimer.d.ts +12 -0
  113. package/dist/detect/disclaimer.d.ts.map +1 -0
  114. package/dist/detect/disclaimer.js +28 -0
  115. package/dist/detect/disclaimer.js.map +1 -0
  116. package/dist/detect/forward-header.d.ts +10 -0
  117. package/dist/detect/forward-header.d.ts.map +1 -0
  118. package/dist/detect/forward-header.js +35 -0
  119. package/dist/detect/forward-header.js.map +1 -0
  120. package/dist/detect/patterns/disclaimers.json +113 -0
  121. package/dist/detect/patterns/forward-headers.json +40 -0
  122. package/dist/detect/patterns/html-clients.json +157 -0
  123. package/dist/detect/patterns/quoted-headers.json +41 -0
  124. package/dist/detect/patterns/reply-headers.json +139 -0
  125. package/dist/detect/patterns/signatures.json +58 -0
  126. package/dist/detect/patterns.d.ts +43 -0
  127. package/dist/detect/patterns.d.ts.map +1 -0
  128. package/dist/detect/patterns.js +125 -0
  129. package/dist/detect/patterns.js.map +1 -0
  130. package/dist/detect/reply-header.d.ts +44 -0
  131. package/dist/detect/reply-header.d.ts.map +1 -0
  132. package/dist/detect/reply-header.js +134 -0
  133. package/dist/detect/reply-header.js.map +1 -0
  134. package/dist/detect/signature.d.ts +28 -0
  135. package/dist/detect/signature.d.ts.map +1 -0
  136. package/dist/detect/signature.js +77 -0
  137. package/dist/detect/signature.js.map +1 -0
  138. package/dist/detect/traffic.d.ts +26 -0
  139. package/dist/detect/traffic.d.ts.map +1 -0
  140. package/dist/detect/traffic.js +98 -0
  141. package/dist/detect/traffic.js.map +1 -0
  142. package/dist/eval/metrics.d.ts +87 -0
  143. package/dist/eval/metrics.d.ts.map +1 -0
  144. package/dist/eval/metrics.js +185 -0
  145. package/dist/eval/metrics.js.map +1 -0
  146. package/dist/index.d.ts +54 -0
  147. package/dist/index.d.ts.map +1 -0
  148. package/dist/index.js +163 -0
  149. package/dist/index.js.map +1 -0
  150. package/dist/match/history.d.ts +62 -0
  151. package/dist/match/history.d.ts.map +1 -0
  152. package/dist/match/history.js +128 -0
  153. package/dist/match/history.js.map +1 -0
  154. package/dist/match/normalize.d.ts +33 -0
  155. package/dist/match/normalize.d.ts.map +1 -0
  156. package/dist/match/normalize.js +63 -0
  157. package/dist/match/normalize.js.map +1 -0
  158. package/dist/match/shingle.d.ts +44 -0
  159. package/dist/match/shingle.d.ts.map +1 -0
  160. package/dist/match/shingle.js +78 -0
  161. package/dist/match/shingle.js.map +1 -0
  162. package/dist/mime/charset.d.ts +33 -0
  163. package/dist/mime/charset.d.ts.map +1 -0
  164. package/dist/mime/charset.js +118 -0
  165. package/dist/mime/charset.js.map +1 -0
  166. package/dist/mime/headers.d.ts +32 -0
  167. package/dist/mime/headers.d.ts.map +1 -0
  168. package/dist/mime/headers.js +127 -0
  169. package/dist/mime/headers.js.map +1 -0
  170. package/dist/mime/model.d.ts +111 -0
  171. package/dist/mime/model.d.ts.map +1 -0
  172. package/dist/mime/model.js +17 -0
  173. package/dist/mime/model.js.map +1 -0
  174. package/dist/mime/parse.d.ts +27 -0
  175. package/dist/mime/parse.d.ts.map +1 -0
  176. package/dist/mime/parse.js +343 -0
  177. package/dist/mime/parse.js.map +1 -0
  178. package/dist/segment/features.d.ts +29 -0
  179. package/dist/segment/features.d.ts.map +1 -0
  180. package/dist/segment/features.js +102 -0
  181. package/dist/segment/features.js.map +1 -0
  182. package/dist/segment/html-blocks.d.ts +28 -0
  183. package/dist/segment/html-blocks.d.ts.map +1 -0
  184. package/dist/segment/html-blocks.js +596 -0
  185. package/dist/segment/html-blocks.js.map +1 -0
  186. package/dist/segment/text-blocks.d.ts +30 -0
  187. package/dist/segment/text-blocks.d.ts.map +1 -0
  188. package/dist/segment/text-blocks.js +204 -0
  189. package/dist/segment/text-blocks.js.map +1 -0
  190. package/dist/segment/types.d.ts +88 -0
  191. package/dist/segment/types.d.ts.map +1 -0
  192. package/dist/segment/types.js +11 -0
  193. package/dist/segment/types.js.map +1 -0
  194. package/dist/types.d.ts +85 -0
  195. package/dist/types.d.ts.map +1 -0
  196. package/dist/types.js +17 -0
  197. package/dist/types.js.map +1 -0
  198. package/dist/util/hash.d.ts +49 -0
  199. package/dist/util/hash.d.ts.map +1 -0
  200. package/dist/util/hash.js +0 -0
  201. package/dist/util/hash.js.map +1 -0
  202. package/dist/util/lang.d.ts +25 -0
  203. package/dist/util/lang.d.ts.map +1 -0
  204. package/dist/util/lang.js +135 -0
  205. package/dist/util/lang.js.map +1 -0
  206. package/dist/util/worker-pool.d.ts +51 -0
  207. package/dist/util/worker-pool.d.ts.map +1 -0
  208. package/dist/util/worker-pool.js +155 -0
  209. package/dist/util/worker-pool.js.map +1 -0
  210. package/package.json +74 -0
@@ -0,0 +1,79 @@
1
+ /**
2
+ * Streaming corpus scanner.
3
+ *
4
+ * Requirements this design answers, in order of importance:
5
+ * - one malformed message must never stop a run, so every failure becomes a row
6
+ * - bounded memory, so files are streamed and results are flushed in batches
7
+ * - resumable, so an interrupted run skips what it already recorded
8
+ * - deterministic, so the same seed always produces the same sample
9
+ * - no message content in any log line
10
+ *
11
+ * Backpressure is driven from here rather than inside the pool: the scanner keeps a bounded window
12
+ * of in flight tasks and awaits a completion before dispatching more. That keeps peak memory
13
+ * proportional to the window size rather than to the corpus size.
14
+ */
15
+ export interface ScanOptions {
16
+ root: string;
17
+ dbPath: string;
18
+ /** Number of worker threads. Defaults to cores minus two, minimum one. */
19
+ concurrency?: number;
20
+ /** Deterministic sample size. Zero or undefined scans everything. */
21
+ sample?: number;
22
+ /** Seed for deterministic sampling. */
23
+ seed?: string;
24
+ /** Skip files already recorded by this processing version. Default true. */
25
+ resume?: boolean;
26
+ /** Files larger than this are recorded as oversize and not parsed. Default 32 MiB. */
27
+ maxFileBytes?: number;
28
+ /** Decoded text retained per part. Default 1 MiB. */
29
+ maxTextPartBytes?: number;
30
+ /** Per file timeout in milliseconds. Default 30000. */
31
+ timeoutMs?: number;
32
+ /** Rows buffered before a database write. Default 500. */
33
+ batchSize?: number;
34
+ /** Filename filter. Default matches .eml case insensitively. */
35
+ pattern?: RegExp;
36
+ /** Called with progress updates. */
37
+ onProgress?: (progress: ScanProgress) => void;
38
+ /** Progress callback interval in milliseconds. Default 1000. */
39
+ progressIntervalMs?: number;
40
+ }
41
+ export interface ScanProgress {
42
+ seen: number;
43
+ total: number;
44
+ processed: number;
45
+ failed: number;
46
+ skipped: number;
47
+ elapsedMs: number;
48
+ filesPerSecond: number;
49
+ etaMs: number | null;
50
+ rssBytes: number;
51
+ }
52
+ export interface ScanSummary extends ScanProgress {
53
+ runId: number;
54
+ status: 'completed' | 'interrupted';
55
+ }
56
+ interface Candidate {
57
+ relPath: string;
58
+ size: number;
59
+ mtime: number;
60
+ }
61
+ /**
62
+ * Walk a directory tree and yield candidate files.
63
+ *
64
+ * Uses opendir so that a directory with 320k entries is streamed rather than materialised as one
65
+ * large array by readdir. Unreadable entries are skipped silently: a permission error on one file
66
+ * is not a reason to abort a corpus walk.
67
+ */
68
+ export declare function walkFiles(root: string, pattern: RegExp): AsyncGenerator<Candidate>;
69
+ /**
70
+ * Select a deterministic sample.
71
+ *
72
+ * Each path is mapped to a uniform value by hashing it with the seed, and the lowest `count` values
73
+ * win. The result depends only on the path set and the seed, never on iteration order, so a sample
74
+ * is reproducible across machines and across partial runs.
75
+ */
76
+ export declare function selectSample(candidates: readonly Candidate[], count: number, seed: string): Candidate[];
77
+ export declare function scanCorpus(options: ScanOptions): Promise<ScanSummary>;
78
+ export {};
79
+ //# sourceMappingURL=scanner.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scanner.d.ts","sourceRoot":"","sources":["../../src/corpus/scanner.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAUH,MAAM,WAAW,WAAW;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,0EAA0E;IAC1E,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,qEAAqE;IACrE,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,uCAAuC;IACvC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,4EAA4E;IAC5E,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,sFAAsF;IACtF,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,qDAAqD;IACrD,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,uDAAuD;IACvD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,0DAA0D;IAC1D,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,gEAAgE;IAChE,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,oCAAoC;IACpC,UAAU,CAAC,EAAE,CAAC,QAAQ,EAAE,YAAY,KAAK,IAAI,CAAC;IAC9C,gEAAgE;IAChE,kBAAkB,CAAC,EAAE,MAAM,CAAC;CAC/B;AAED,MAAM,WAAW,YAAY;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,cAAc,EAAE,MAAM,CAAC;IACvB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,QAAQ,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,WAAY,SAAQ,YAAY;IAC7C,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,WAAW,GAAG,aAAa,CAAC;CACvC;AAED,UAAU,SAAS;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;CACjB;AAED;;;;;;GAMG;AACH,wBAAuB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,cAAc,CAAC,SAAS,CAAC,CAgCzF;AAED;;;;;;GAMG;AACH,wBAAgB,YAAY,CAAC,UAAU,EAAE,SAAS,SAAS,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,SAAS,EAAE,CAOvG;AAMD,wBAAsB,UAAU,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,WAAW,CAAC,CAqL3E"}
@@ -0,0 +1,256 @@
1
+ "use strict";
2
+ /**
3
+ * Streaming corpus scanner.
4
+ *
5
+ * Requirements this design answers, in order of importance:
6
+ * - one malformed message must never stop a run, so every failure becomes a row
7
+ * - bounded memory, so files are streamed and results are flushed in batches
8
+ * - resumable, so an interrupted run skips what it already recorded
9
+ * - deterministic, so the same seed always produces the same sample
10
+ * - no message content in any log line
11
+ *
12
+ * Backpressure is driven from here rather than inside the pool: the scanner keeps a bounded window
13
+ * of in flight tasks and awaits a completion before dispatching more. That keeps peak memory
14
+ * proportional to the window size rather than to the corpus size.
15
+ */
16
+ var __importDefault = (this && this.__importDefault) || function (mod) {
17
+ return (mod && mod.__esModule) ? mod : { "default": mod };
18
+ };
19
+ Object.defineProperty(exports, "__esModule", { value: true });
20
+ exports.walkFiles = walkFiles;
21
+ exports.selectSample = selectSample;
22
+ exports.scanCorpus = scanCorpus;
23
+ const node_fs_1 = __importDefault(require("node:fs"));
24
+ const node_path_1 = __importDefault(require("node:path"));
25
+ const node_os_1 = __importDefault(require("node:os"));
26
+ const hash_1 = require("../util/hash");
27
+ const worker_pool_1 = require("../util/worker-pool");
28
+ const store_1 = require("./store");
29
+ /**
30
+ * Walk a directory tree and yield candidate files.
31
+ *
32
+ * Uses opendir so that a directory with 320k entries is streamed rather than materialised as one
33
+ * large array by readdir. Unreadable entries are skipped silently: a permission error on one file
34
+ * is not a reason to abort a corpus walk.
35
+ */
36
+ async function* walkFiles(root, pattern) {
37
+ const stack = [''];
38
+ while (stack.length) {
39
+ const relDir = stack.pop();
40
+ const absDir = node_path_1.default.join(root, relDir);
41
+ let dir;
42
+ try {
43
+ dir = node_fs_1.default.opendirSync(absDir);
44
+ }
45
+ catch {
46
+ continue;
47
+ }
48
+ try {
49
+ for await (const entry of dir) {
50
+ const relPath = relDir ? node_path_1.default.join(relDir, entry.name) : entry.name;
51
+ if (entry.isDirectory()) {
52
+ stack.push(relPath);
53
+ continue;
54
+ }
55
+ if (!entry.isFile() || !pattern.test(entry.name)) {
56
+ continue;
57
+ }
58
+ try {
59
+ const stat = node_fs_1.default.statSync(node_path_1.default.join(root, relPath));
60
+ yield { relPath, size: stat.size, mtime: Math.floor(stat.mtimeMs) };
61
+ }
62
+ catch {
63
+ // Vanished between readdir and stat. Nothing to record.
64
+ }
65
+ }
66
+ }
67
+ catch {
68
+ // Directory iteration failed midway. Keep whatever was yielded and move on.
69
+ }
70
+ }
71
+ }
72
+ /**
73
+ * Select a deterministic sample.
74
+ *
75
+ * Each path is mapped to a uniform value by hashing it with the seed, and the lowest `count` values
76
+ * win. The result depends only on the path set and the seed, never on iteration order, so a sample
77
+ * is reproducible across machines and across partial runs.
78
+ */
79
+ function selectSample(candidates, count, seed) {
80
+ if (!count || count >= candidates.length) {
81
+ return [...candidates];
82
+ }
83
+ const scored = candidates.map(candidate => ({ candidate, score: (0, hash_1.deterministicUnit)(seed, candidate.relPath) }));
84
+ scored.sort((a, b) => a.score - b.score || (a.candidate.relPath < b.candidate.relPath ? -1 : 1));
85
+ return scored.slice(0, count).map(item => item.candidate);
86
+ }
87
+ function resolveWorkerPath() {
88
+ return node_path_1.default.join(__dirname, 'worker.js');
89
+ }
90
+ async function scanCorpus(options) {
91
+ const root = node_path_1.default.resolve(options.root);
92
+ const pattern = options.pattern ?? /\.eml$/i;
93
+ const concurrency = Math.max(1, options.concurrency ?? Math.max(1, node_os_1.default.availableParallelism() - 2));
94
+ const batchSize = options.batchSize ?? 500;
95
+ const timeoutMs = options.timeoutMs ?? 30000;
96
+ const maxFileBytes = options.maxFileBytes ?? 32 * 1024 * 1024;
97
+ const maxTextPartBytes = options.maxTextPartBytes ?? 1024 * 1024;
98
+ const progressIntervalMs = options.progressIntervalMs ?? 1000;
99
+ const resume = options.resume !== false;
100
+ const store = new store_1.CorpusStore(options.dbPath);
101
+ // The candidate list is collected first so that sampling is deterministic and progress can
102
+ // report a real total. Only path, size and mtime are held, roughly 100 bytes per file, which is
103
+ // about 32 MB for the full corpus. Message bodies are never held.
104
+ const candidates = [];
105
+ for await (const candidate of walkFiles(root, pattern)) {
106
+ candidates.push(candidate);
107
+ }
108
+ candidates.sort((a, b) => (a.relPath < b.relPath ? -1 : a.relPath > b.relPath ? 1 : 0));
109
+ const selected = options.sample ? selectSample(candidates, options.sample, options.seed ?? 'default') : candidates;
110
+ const runId = store.startRun({
111
+ root,
112
+ concurrency,
113
+ sample: options.sample ?? null,
114
+ seed: options.seed ?? null,
115
+ resume,
116
+ maxFileBytes,
117
+ maxTextPartBytes,
118
+ timeoutMs,
119
+ total: selected.length
120
+ });
121
+ const pool = new worker_pool_1.WorkerPool({
122
+ workerPath: resolveWorkerPath(),
123
+ size: concurrency,
124
+ taskTimeoutMs: timeoutMs,
125
+ // Generous relative to the working set of one message, tight enough that the whole pool
126
+ // stays well under a gigabyte.
127
+ maxOldGenerationSizeMb: 192,
128
+ workerData: { root, salt: store.salt, maxFileBytes, maxTextPartBytes }
129
+ });
130
+ pool.on('worker-error', () => {
131
+ // Reported through the failed counter. Worker errors carry no message content, but there is
132
+ // nothing actionable to print per occurrence during a long run.
133
+ });
134
+ const started = Date.now();
135
+ let seen = 0;
136
+ let processed = 0;
137
+ let failed = 0;
138
+ let skipped = 0;
139
+ let interrupted = false;
140
+ let batch = [];
141
+ const flush = () => {
142
+ if (!batch.length) {
143
+ return;
144
+ }
145
+ const rows = batch;
146
+ batch = [];
147
+ store.writeBatch(rows, runId);
148
+ };
149
+ const progress = () => {
150
+ const elapsedMs = Date.now() - started;
151
+ const done = processed + failed;
152
+ const rate = elapsedMs > 0 ? (done / elapsedMs) * 1000 : 0;
153
+ const remaining = selected.length - seen;
154
+ return {
155
+ seen,
156
+ total: selected.length,
157
+ processed,
158
+ failed,
159
+ skipped,
160
+ elapsedMs,
161
+ filesPerSecond: rate,
162
+ etaMs: rate > 0 ? (remaining / rate) * 1000 : null,
163
+ rssBytes: process.memoryUsage.rss()
164
+ };
165
+ };
166
+ let lastProgress = 0;
167
+ const maybeReport = () => {
168
+ if (!options.onProgress) {
169
+ return;
170
+ }
171
+ const now = Date.now();
172
+ if (now - lastProgress >= progressIntervalMs) {
173
+ lastProgress = now;
174
+ options.onProgress(progress());
175
+ }
176
+ };
177
+ const onSignal = () => {
178
+ interrupted = true;
179
+ };
180
+ process.on('SIGINT', onSignal);
181
+ process.on('SIGTERM', onSignal);
182
+ const window = concurrency * 4;
183
+ const inFlight = new Set();
184
+ try {
185
+ for (const candidate of selected) {
186
+ if (interrupted) {
187
+ break;
188
+ }
189
+ seen++;
190
+ if (resume && store.isProcessed(candidate.relPath, candidate.size, candidate.mtime)) {
191
+ skipped++;
192
+ maybeReport();
193
+ continue;
194
+ }
195
+ const task = { relPath: candidate.relPath, size: candidate.size, mtime: candidate.mtime };
196
+ const promise = pool
197
+ .submit(task)
198
+ .then(result => {
199
+ batch.push({ file: result.file, message: result.message });
200
+ if (result.file.status === 'ok') {
201
+ processed++;
202
+ }
203
+ else {
204
+ failed++;
205
+ }
206
+ })
207
+ .catch((err) => {
208
+ // A rejected task means the worker died or timed out. Record it and continue.
209
+ failed++;
210
+ batch.push({
211
+ file: {
212
+ path: candidate.relPath,
213
+ size: candidate.size,
214
+ mtime: candidate.mtime,
215
+ sha256: null,
216
+ status: err.name === 'WorkerTimeoutError' ? 'timeout' : 'failed',
217
+ errorCode: err.name === 'WorkerTimeoutError' ? 'timeout' : 'worker_error',
218
+ errorDetail: err.message.slice(0, 300),
219
+ durationMs: null
220
+ },
221
+ message: null
222
+ });
223
+ })
224
+ .then(() => {
225
+ if (batch.length >= batchSize) {
226
+ flush();
227
+ }
228
+ maybeReport();
229
+ });
230
+ inFlight.add(promise);
231
+ void promise.finally(() => inFlight.delete(promise));
232
+ if (inFlight.size >= window) {
233
+ await Promise.race(inFlight);
234
+ }
235
+ }
236
+ await Promise.all(inFlight);
237
+ flush();
238
+ }
239
+ finally {
240
+ process.off('SIGINT', onSignal);
241
+ process.off('SIGTERM', onSignal);
242
+ await pool.close();
243
+ flush();
244
+ const final = progress();
245
+ store.finishRun(runId, interrupted ? 'interrupted' : 'completed', {
246
+ seen: final.seen,
247
+ processed: final.processed,
248
+ failed: final.failed,
249
+ skipped: final.skipped
250
+ });
251
+ store.close();
252
+ }
253
+ const final = progress();
254
+ return { ...final, runId, status: interrupted ? 'interrupted' : 'completed' };
255
+ }
256
+ //# sourceMappingURL=scanner.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scanner.js","sourceRoot":"","sources":["../../src/corpus/scanner.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,76 @@
1
+ /**
2
+ * Aggregate corpus statistics.
3
+ *
4
+ * Everything here is a count, a ratio or a percentile. No subject, address or body text is read,
5
+ * and the only free text that reaches the report is a MIME structure signature and detector ids,
6
+ * both of which are drawn from a closed vocabulary. That is what makes the report safe to paste
7
+ * into a document or an issue.
8
+ */
9
+ import { type CorpusStore } from './store';
10
+ export interface Distribution {
11
+ label: string;
12
+ count: number;
13
+ share: number;
14
+ }
15
+ export interface CorpusStats {
16
+ generatedAt: string;
17
+ files: {
18
+ total: number;
19
+ ok: number;
20
+ failed: number;
21
+ oversize: number;
22
+ timeout: number;
23
+ skipped: number;
24
+ };
25
+ errors: Distribution[];
26
+ sizeBytes: {
27
+ p10: number;
28
+ p50: number;
29
+ p90: number;
30
+ p99: number;
31
+ max: number;
32
+ total: number;
33
+ };
34
+ parseWarnings: Distribution[];
35
+ bodyKind: Distribution[];
36
+ structures: Distribution[];
37
+ charsets: Distribution[];
38
+ encodings: Distribution[];
39
+ traffic: Distribution[];
40
+ clients: Distribution[];
41
+ clientSources: Distribution[];
42
+ subjectPrefix: Distribution[];
43
+ replyStyle: Distribution[];
44
+ quoteDepth: Distribution[];
45
+ markers: Distribution[];
46
+ htmlHints: Distribution[];
47
+ languages: Distribution[];
48
+ duplicates: {
49
+ distinctSha256: number;
50
+ filesInDuplicateGroups: number;
51
+ distinctSimhash: number;
52
+ filesInNearDuplicateGroups: number;
53
+ };
54
+ threading: {
55
+ withMessageId: number;
56
+ withInReplyTo: number;
57
+ parentResolvedInCorpus: number;
58
+ withReferences: number;
59
+ ancestorResolvedInCorpus: number;
60
+ };
61
+ humanSlice: {
62
+ total: number;
63
+ replies: number;
64
+ repliesWithParentInCorpus: number;
65
+ bothRepresentations: number;
66
+ quoteMarkingDisagrees: number;
67
+ };
68
+ contentAgreement: {
69
+ messagesWithBoth: number;
70
+ quoteMarkingDisagrees: number;
71
+ };
72
+ }
73
+ export declare function computeStats(store: CorpusStore): CorpusStats;
74
+ /** Render the statistics as a Markdown report. Safe to share: contains no message content. */
75
+ export declare function renderReport(stats: CorpusStats, root: string): string;
76
+ //# sourceMappingURL=stats.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stats.d.ts","sourceRoot":"","sources":["../../src/corpus/stats.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,KAAK,WAAW,EAAE,MAAM,SAAS,CAAC;AAE3C,MAAM,WAAW,YAAY;IACzB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,WAAW;IACxB,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE;QACH,KAAK,EAAE,MAAM,CAAC;QACd,EAAE,EAAE,MAAM,CAAC;QACX,MAAM,EAAE,MAAM,CAAC;QACf,QAAQ,EAAE,MAAM,CAAC;QACjB,OAAO,EAAE,MAAM,CAAC;QAChB,OAAO,EAAE,MAAM,CAAC;KACnB,CAAC;IACF,MAAM,EAAE,YAAY,EAAE,CAAC;IACvB,SAAS,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC;IAC9F,aAAa,EAAE,YAAY,EAAE,CAAC;IAC9B,QAAQ,EAAE,YAAY,EAAE,CAAC;IACzB,UAAU,EAAE,YAAY,EAAE,CAAC;IAC3B,QAAQ,EAAE,YAAY,EAAE,CAAC;IACzB,SAAS,EAAE,YAAY,EAAE,CAAC;IAC1B,OAAO,EAAE,YAAY,EAAE,CAAC;IACxB,OAAO,EAAE,YAAY,EAAE,CAAC;IACxB,aAAa,EAAE,YAAY,EAAE,CAAC;IAC9B,aAAa,EAAE,YAAY,EAAE,CAAC;IAC9B,UAAU,EAAE,YAAY,EAAE,CAAC;IAC3B,UAAU,EAAE,YAAY,EAAE,CAAC;IAC3B,OAAO,EAAE,YAAY,EAAE,CAAC;IACxB,SAAS,EAAE,YAAY,EAAE,CAAC;IAC1B,SAAS,EAAE,YAAY,EAAE,CAAC;IAC1B,UAAU,EAAE;QACR,cAAc,EAAE,MAAM,CAAC;QACvB,sBAAsB,EAAE,MAAM,CAAC;QAC/B,eAAe,EAAE,MAAM,CAAC;QACxB,0BAA0B,EAAE,MAAM,CAAC;KACtC,CAAC;IACF,SAAS,EAAE;QACP,aAAa,EAAE,MAAM,CAAC;QACtB,aAAa,EAAE,MAAM,CAAC;QACtB,sBAAsB,EAAE,MAAM,CAAC;QAC/B,cAAc,EAAE,MAAM,CAAC;QACvB,wBAAwB,EAAE,MAAM,CAAC;KACpC,CAAC;IACF,UAAU,EAAE;QACR,KAAK,EAAE,MAAM,CAAC;QACd,OAAO,EAAE,MAAM,CAAC;QAChB,yBAAyB,EAAE,MAAM,CAAC;QAClC,mBAAmB,EAAE,MAAM,CAAC;QAC5B,qBAAqB,EAAE,MAAM,CAAC;KACjC,CAAC;IACF,gBAAgB,EAAE;QACd,gBAAgB,EAAE,MAAM,CAAC;QACzB,qBAAqB,EAAE,MAAM,CAAC;KACjC,CAAC;CACL;AA8DD,wBAAgB,YAAY,CAAC,KAAK,EAAE,WAAW,GAAG,WAAW,CA8G5D;AAkCD,8FAA8F;AAC9F,wBAAgB,YAAY,CAAC,KAAK,EAAE,WAAW,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CA+GrE"}