@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.
Files changed (3) hide show
  1. package/package.json +5 -5
  2. package/store.d.ts +5 -0
  3. 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.89",
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.57",
13
- "@bobfrankston/mailx-settings": "^0.1.58",
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.57",
33
- "@bobfrankston/mailx-settings": "^0.1.58",
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.