@bobfrankston/mailx-types 0.1.73 → 0.1.76

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/index.d.ts CHANGED
@@ -5,6 +5,7 @@
5
5
  */
6
6
  export { CONTACT_RULES } from "./contact-rules.js";
7
7
  export { assessMessageTrust, spamScoreOf } from "./trust.js";
8
+ export { logIfChanged, clearLogState } from "./log-changes.js";
8
9
  export type { TrustFinding, TrustInput, SpamScore } from "./trust.js";
9
10
  export type { MailxApi } from "./mailx-api.js";
10
11
  export { addContactsDenylistEntry, addContactsPreferredEntry, type PreferredContactEntry, type CloudReadFn, type CloudWriteFn, } from "./contacts-config.js";
package/index.js CHANGED
@@ -8,6 +8,7 @@
8
8
  // junk-contact filtering on every platform.
9
9
  export { CONTACT_RULES } from "./contact-rules.js";
10
10
  export { assessMessageTrust, spamScoreOf } from "./trust.js";
11
+ export { logIfChanged, clearLogState } from "./log-changes.js";
11
12
  // Shared contacts.jsonc mutations — one implementation for desktop + Android.
12
13
  export { addContactsDenylistEntry, addContactsPreferredEntry, } from "./contacts-config.js";
13
14
  // Group-name expansion for recipient fields. Lets users type a group name
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Log a line only when it says something new.
3
+ *
4
+ * Sync walks ~90 folders every cycle, and a folder in a bad state produces the
5
+ * SAME line every time — "reconcile refused — would delete 250/250 (100%)"
6
+ * once per folder per cycle, forever. On Android each of those is an HTTP
7
+ * request to the log server, and in the file the real news is buried under
8
+ * hundreds of identical repeats (Bob 2026-08-29: "are we overlogging").
9
+ *
10
+ * Deleting the lines is the wrong fix — a refused reconcile is a genuine
11
+ * anomaly and the log is where it surfaces. What is worth reading is the
12
+ * CHANGE: the first time a folder enters that state, and every time the
13
+ * numbers move. So the same key + same text is dropped, a changed text is
14
+ * printed, and a state that persists is re-stated once an hour so a standing
15
+ * problem cannot become invisible.
16
+ *
17
+ * Keyed by caller-supplied string (account + folder + which check), because
18
+ * two folders in the same state are two facts, not one.
19
+ */
20
+ /**
21
+ * @param key identifies the thing being described — "acct/folder:reconcile"
22
+ * @param line the message; identical text for the same key is suppressed
23
+ * @param emit where to write (defaults to console.log; pass a logger in libraries)
24
+ * @returns whether the line was emitted, so callers can count what they skipped
25
+ */
26
+ export declare function logIfChanged(key: string, line: string, emit?: (msg: string) => void): boolean;
27
+ /** Forget a key — call when the condition clears, so its return is news again. */
28
+ export declare function clearLogState(key: string): void;
29
+ //# sourceMappingURL=log-changes.d.ts.map
package/log-changes.js ADDED
@@ -0,0 +1,42 @@
1
+ /**
2
+ * Log a line only when it says something new.
3
+ *
4
+ * Sync walks ~90 folders every cycle, and a folder in a bad state produces the
5
+ * SAME line every time — "reconcile refused — would delete 250/250 (100%)"
6
+ * once per folder per cycle, forever. On Android each of those is an HTTP
7
+ * request to the log server, and in the file the real news is buried under
8
+ * hundreds of identical repeats (Bob 2026-08-29: "are we overlogging").
9
+ *
10
+ * Deleting the lines is the wrong fix — a refused reconcile is a genuine
11
+ * anomaly and the log is where it surfaces. What is worth reading is the
12
+ * CHANGE: the first time a folder enters that state, and every time the
13
+ * numbers move. So the same key + same text is dropped, a changed text is
14
+ * printed, and a state that persists is re-stated once an hour so a standing
15
+ * problem cannot become invisible.
16
+ *
17
+ * Keyed by caller-supplied string (account + folder + which check), because
18
+ * two folders in the same state are two facts, not one.
19
+ */
20
+ /** How long an unchanged line stays suppressed before being restated. */
21
+ const RESTATE_AFTER_MS = 60 * 60 * 1000;
22
+ const lastSeen = new Map();
23
+ /**
24
+ * @param key identifies the thing being described — "acct/folder:reconcile"
25
+ * @param line the message; identical text for the same key is suppressed
26
+ * @param emit where to write (defaults to console.log; pass a logger in libraries)
27
+ * @returns whether the line was emitted, so callers can count what they skipped
28
+ */
29
+ export function logIfChanged(key, line, emit = console.log) {
30
+ const now = Date.now();
31
+ const prev = lastSeen.get(key);
32
+ if (prev && prev.line === line && now - prev.at < RESTATE_AFTER_MS)
33
+ return false;
34
+ lastSeen.set(key, { line, at: now });
35
+ emit(prev && prev.line === line ? `${line} (unchanged for an hour)` : line);
36
+ return true;
37
+ }
38
+ /** Forget a key — call when the condition clears, so its return is news again. */
39
+ export function clearLogState(key) {
40
+ lastSeen.delete(key);
41
+ }
42
+ //# sourceMappingURL=log-changes.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bobfrankston/mailx-types",
3
- "version": "0.1.73",
3
+ "version": "0.1.76",
4
4
  "type": "module",
5
5
  "main": "index.js",
6
6
  "types": "index.d.ts",