@yozz.app/smtp 0.1.2 → 0.1.3

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/README.md CHANGED
@@ -14,7 +14,7 @@ keywords, AUTH PLAIN / LOGIN, the MAIL / RCPT / DATA sequence and dot-stuffing.
14
14
  TLS, certificates or the vault. STARTTLS is not spoken: the transport is already TLS (465).
15
15
 
16
16
  `buildMessage` turns composer fields into 7-bit bytes: RFC 2047 headers, 7bit or quoted-printable bodies,
17
- `multipart/alternative` when an HTML rendering is given, `In-Reply-To` + `References` for replies.
17
+ `multipart/alternative` when an HTML rendering is given, `In-Reply-To` + the folded `References` chain for replies.
18
18
 
19
19
  ## Tests
20
20
 
package/dist/index.d.mts CHANGED
@@ -77,8 +77,25 @@ type MessageInput = {
77
77
  readonly messageId: string;
78
78
  readonly text: string; /** When present the message is `multipart/alternative`, text first. */
79
79
  readonly html?: string;
80
- readonly inReplyTo?: string; /** When present the whole message becomes `multipart/mixed`: the body first, then each file. */
80
+ readonly inReplyTo?: string;
81
+ /**
82
+ * The parent's `References` followed by its Message-ID, oldest first — the chain RFC 5322 §3.6.4
83
+ * asks a reply to carry. Without it a client that threads on References alone (and the base
84
+ * subject is a guess, not a rule) sees a reply to a reply as a new conversation. Defaults to
85
+ * `[inReplyTo]`, which is what a first reply's chain is anyway.
86
+ */
87
+ readonly references?: readonly string[]; /** When present the whole message becomes `multipart/mixed`: the body first, then each file. */
81
88
  readonly attachments?: readonly MessageAttachment[];
89
+ /**
90
+ * Headers this client owns, appended after the standard block.
91
+ *
92
+ * YOZZ no longer uses this. It stamped `X-Yozz-Draft` here to find its own copies again without
93
+ * trusting a Message-ID a provider may rewrite, and that failed on a more basic point: a server
94
+ * need only index the headers IMAP names, so `SEARCH HEADER` on a private one can answer the
95
+ * empty list for a message that carries it (docs/knowledge/forwardemail-api.md). The field stays
96
+ * because a message builder should be able to set a header; do not build a LOOKUP on one.
97
+ */
98
+ readonly extraHeaders?: readonly (readonly [string, string])[];
82
99
  };
83
100
  type MessageAttachment = {
84
101
  readonly filename: string;
package/dist/index.mjs CHANGED
@@ -435,7 +435,16 @@ const buildMessage = (input) => {
435
435
  const cc = input.cc ?? [];
436
436
  if (cc.length > 0) headers.push(["Cc", cc.join(", ")]);
437
437
  headers.push(["Subject", encodeHeaderText(input.subject)], ["Date", formatDate(input.date)], ["Message-ID", input.messageId], ["MIME-Version", "1.0"]);
438
- if (input.inReplyTo !== void 0) headers.push(["In-Reply-To", input.inReplyTo], ["References", input.inReplyTo]);
438
+ if (input.inReplyTo !== void 0) headers.push(["In-Reply-To", input.inReplyTo]);
439
+ const references = input.references ?? (input.inReplyTo === void 0 ? [] : [input.inReplyTo]);
440
+ if (references.length > 0) {
441
+ for (const id of references) assertNoLineBreak("References", id);
442
+ headers.push(["References", references.join("\r\n ")]);
443
+ }
444
+ for (const [name, value] of input.extraHeaders ?? []) {
445
+ assertNoLineBreak(name, value);
446
+ headers.push([name, value]);
447
+ }
439
448
  for (const { filename, mimeType } of input.attachments ?? []) {
440
449
  assertNoLineBreak("filename", filename);
441
450
  assertNoLineBreak("Content-Type", mimeType);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yozz.app/smtp",
3
- "version": "0.1.2",
3
+ "version": "0.1.3",
4
4
  "description": "Transport-agnostic SMTP client core plus an RFC 5322 message builder.",
5
5
  "license": "MIT",
6
6
  "homepage": "https://github.com/fishballapp/yozz",
@@ -31,8 +31,8 @@
31
31
  "devDependencies": {
32
32
  "@types/node": "^24.0.0",
33
33
  "tsdown": "^0.22.3",
34
- "@yozz.app/tls": "0.1.2",
35
- "@yozz.app/x509": "0.1.2"
34
+ "@yozz.app/tls": "0.1.3",
35
+ "@yozz.app/x509": "0.1.3"
36
36
  },
37
37
  "scripts": {
38
38
  "build": "tsdown src/index.ts --format esm --dts",