@bobfrankston/mailx-store 0.1.89 → 0.1.91
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/package.json +5 -5
- package/store.d.ts +5 -0
- package/store.js +21 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bobfrankston/mailx-store",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.91",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"types": "index.d.ts",
|
|
@@ -9,8 +9,8 @@
|
|
|
9
9
|
},
|
|
10
10
|
"license": "ISC",
|
|
11
11
|
"dependencies": {
|
|
12
|
-
"@bobfrankston/mailx-types": "^0.1.
|
|
13
|
-
"@bobfrankston/mailx-settings": "^0.1.
|
|
12
|
+
"@bobfrankston/mailx-types": "^0.1.59",
|
|
13
|
+
"@bobfrankston/mailx-settings": "^0.1.59",
|
|
14
14
|
"@bobfrankston/mailx-bus": "^0.1.2",
|
|
15
15
|
"mailparser": "^3.7.2"
|
|
16
16
|
},
|
|
@@ -29,8 +29,8 @@
|
|
|
29
29
|
},
|
|
30
30
|
".transformedSnapshot": {
|
|
31
31
|
"dependencies": {
|
|
32
|
-
"@bobfrankston/mailx-types": "^0.1.
|
|
33
|
-
"@bobfrankston/mailx-settings": "^0.1.
|
|
32
|
+
"@bobfrankston/mailx-types": "^0.1.59",
|
|
33
|
+
"@bobfrankston/mailx-settings": "^0.1.59",
|
|
34
34
|
"@bobfrankston/mailx-bus": "^0.1.2",
|
|
35
35
|
"mailparser": "^3.7.2"
|
|
36
36
|
}
|
package/store.d.ts
CHANGED
|
@@ -19,6 +19,7 @@
|
|
|
19
19
|
* be able to consume it without depending on mailx-service — that's the
|
|
20
20
|
* "arrow points the right way" property of the architecture.
|
|
21
21
|
*/
|
|
22
|
+
import type { ParsedInvite } from "@bobfrankston/mailx-types";
|
|
22
23
|
import { MailxDB } from "./db.js";
|
|
23
24
|
import { FileMessageStore } from "./file-store.js";
|
|
24
25
|
import type { StoreBus } from "./bus.js";
|
|
@@ -63,6 +64,10 @@ export interface StoreMessage extends MessageEnvelope {
|
|
|
63
64
|
* back into saveDraft so the server-side dedup keeps replacing the
|
|
64
65
|
* same draft instead of stacking up a new one every save. */
|
|
65
66
|
mailxDraftId: string;
|
|
67
|
+
/** Parsed VEVENT when the message carries a text/calendar part, else null.
|
|
68
|
+
* Lets the viewer render an invite card instead of handing invite.ics to
|
|
69
|
+
* whatever program happens to own .ics (Bob 2026-08-22). */
|
|
70
|
+
invite: ParsedInvite | null;
|
|
66
71
|
}
|
|
67
72
|
export declare class Store {
|
|
68
73
|
/** SQLite metadata index. Exposed as a public field — sync clients
|
package/store.js
CHANGED
|
@@ -21,6 +21,7 @@
|
|
|
21
21
|
*/
|
|
22
22
|
import * as fs from "node:fs";
|
|
23
23
|
import { parseSerial } from "./parse-serial.js";
|
|
24
|
+
import { parseInvite } from "@bobfrankston/mailx-types";
|
|
24
25
|
import { storeBus } from "./bus.js";
|
|
25
26
|
import { sanitizeHtml, htmlToPlainText } from "@bobfrankston/mailx-types";
|
|
26
27
|
import { loadSettings, loadAllowlist } from "@bobfrankston/mailx-settings";
|
|
@@ -291,6 +292,7 @@ export class Store {
|
|
|
291
292
|
hasRemoteContent: false, remoteAllowed: allowRemote,
|
|
292
293
|
attachments: [],
|
|
293
294
|
cached: false,
|
|
295
|
+
invite: null,
|
|
294
296
|
bodyOnDisk: false, // overridden to true on the parse-pending return
|
|
295
297
|
deliveredTo: "", returnPath: "",
|
|
296
298
|
listUnsubscribe: "", listUnsubscribeMail: "", listUnsubscribeHttp: "", listUnsubscribeOneClick: false,
|
|
@@ -445,6 +447,24 @@ export class Store {
|
|
|
445
447
|
size: x.a.size || 0,
|
|
446
448
|
contentId: x.a.contentId || "",
|
|
447
449
|
}));
|
|
450
|
+
// Calendar invite. Read from the RAW attachment list, not the filtered
|
|
451
|
+
// one above: a text/calendar alternative has no filename and inline
|
|
452
|
+
// disposition, so the spurious-text-part filter (correctly) drops it
|
|
453
|
+
// from the attachment chips — which would also hide it from us.
|
|
454
|
+
// A malformed invite must never break rendering the message.
|
|
455
|
+
let invite = null;
|
|
456
|
+
try {
|
|
457
|
+
const calPart = (parsed.attachments || []).find((a) => /text\/calendar|application\/ics/i.test(a.contentType || "")
|
|
458
|
+
|| /\.ics$/i.test(a.filename || ""));
|
|
459
|
+
if (calPart) {
|
|
460
|
+
const c = calPart.content;
|
|
461
|
+
const text = Buffer.isBuffer(c) ? c.toString("utf8") : String(c ?? "");
|
|
462
|
+
invite = parseInvite(text);
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
catch (e) {
|
|
466
|
+
console.warn("[store] invite parse failed:", e?.message || e);
|
|
467
|
+
}
|
|
448
468
|
// Header extraction — Delivered-To, Return-Path, List-Unsubscribe.
|
|
449
469
|
// Each delivery agent PREPENDS its Delivered-To, so the FIRST
|
|
450
470
|
// (topmost) header is the final delivery to the user's actual
|
|
@@ -532,6 +552,7 @@ export class Store {
|
|
|
532
552
|
emlPath: this.bodyStore.absolutePath(storedPath),
|
|
533
553
|
isFlagged,
|
|
534
554
|
mailxDraftId,
|
|
555
|
+
invite,
|
|
535
556
|
};
|
|
536
557
|
// Memoize for instant re-view of the same message. cacheKey is
|
|
537
558
|
// path+mtime so a re-fetch (mtime changes) invalidates naturally.
|