@upyo/smtp 0.1.0 → 0.1.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.
package/dist/index.cjs CHANGED
@@ -278,10 +278,10 @@ async function buildRawMessage(message) {
278
278
  const hasHtml = "html" in message.content;
279
279
  const hasText = "text" in message.content;
280
280
  const isMultipart = hasAttachments || hasHtml && hasText;
281
- lines.push(`From: ${(0, __upyo_core.formatAddress)(message.sender)}`);
282
- lines.push(`To: ${message.recipients.map(__upyo_core.formatAddress).join(", ")}`);
283
- if (message.ccRecipients.length > 0) lines.push(`Cc: ${message.ccRecipients.map(__upyo_core.formatAddress).join(", ")}`);
284
- if (message.replyRecipients.length > 0) lines.push(`Reply-To: ${message.replyRecipients.map(__upyo_core.formatAddress).join(", ")}`);
281
+ lines.push(`From: ${encodeHeaderValue((0, __upyo_core.formatAddress)(message.sender))}`);
282
+ lines.push(`To: ${encodeHeaderValue(message.recipients.map(__upyo_core.formatAddress).join(", "))}`);
283
+ if (message.ccRecipients.length > 0) lines.push(`Cc: ${encodeHeaderValue(message.ccRecipients.map(__upyo_core.formatAddress).join(", "))}`);
284
+ if (message.replyRecipients.length > 0) lines.push(`Reply-To: ${encodeHeaderValue(message.replyRecipients.map(__upyo_core.formatAddress).join(", "))}`);
285
285
  lines.push(`Subject: ${encodeHeaderValue(message.subject)}`);
286
286
  lines.push(`Date: ${(/* @__PURE__ */ new Date()).toUTCString()}`);
287
287
  lines.push(`Message-ID: <${generateMessageId()}>`);
@@ -365,17 +365,48 @@ function encodeHeaderValue(value) {
365
365
  if (!/^[\x20-\x7E]*$/.test(value)) {
366
366
  const utf8Bytes = new TextEncoder().encode(value);
367
367
  const base64 = btoa(String.fromCharCode(...utf8Bytes));
368
- return `=?UTF-8?B?${base64}?=`;
368
+ const maxEncodedLength = 75;
369
+ const encodedWord = `=?UTF-8?B?${base64}?=`;
370
+ if (encodedWord.length <= maxEncodedLength) return encodedWord;
371
+ const words = [];
372
+ let currentBase64 = "";
373
+ for (let i = 0; i < base64.length; i += 4) {
374
+ const chunk = base64.slice(i, i + 4);
375
+ const testWord = `=?UTF-8?B?${currentBase64}${chunk}?=`;
376
+ if (testWord.length <= maxEncodedLength) currentBase64 += chunk;
377
+ else {
378
+ if (currentBase64) words.push(`=?UTF-8?B?${currentBase64}?=`);
379
+ currentBase64 = chunk;
380
+ }
381
+ }
382
+ if (currentBase64) words.push(`=?UTF-8?B?${currentBase64}?=`);
383
+ return words.join(" ");
369
384
  }
370
385
  return value;
371
386
  }
372
387
  function encodeQuotedPrintable(text) {
373
- return text.replace(/[^\x20-\x7E]/g, (char) => {
374
- const code = char.charCodeAt(0);
375
- if (code < 256) return `=${code.toString(16).toUpperCase().padStart(2, "0")}`;
376
- const utf8 = new TextEncoder().encode(char);
377
- return Array.from(utf8).map((byte) => `=${byte.toString(16).toUpperCase().padStart(2, "0")}`).join("");
378
- }).replace(/=$/gm, "=3D").replace(/^\./, "=2E");
388
+ const utf8Bytes = new TextEncoder().encode(text);
389
+ let result = "";
390
+ let lineLength = 0;
391
+ const maxLineLength = 76;
392
+ for (let i = 0; i < utf8Bytes.length; i++) {
393
+ const byte = utf8Bytes[i];
394
+ let encoded = "";
395
+ if (byte < 32 || byte > 126 || byte === 61 || byte === 46 && lineLength === 0) encoded = `=${byte.toString(16).toUpperCase().padStart(2, "0")}`;
396
+ else encoded = String.fromCharCode(byte);
397
+ if (lineLength + encoded.length > maxLineLength) {
398
+ result += "=\r\n";
399
+ lineLength = 0;
400
+ }
401
+ result += encoded;
402
+ lineLength += encoded.length;
403
+ if (byte === 13 && i + 1 < utf8Bytes.length && utf8Bytes[i + 1] === 10) {
404
+ i++;
405
+ result += String.fromCharCode(10);
406
+ lineLength = 0;
407
+ } else if (byte === 10 && (i === 0 || utf8Bytes[i - 1] !== 13)) lineLength = 0;
408
+ }
409
+ return result;
379
410
  }
380
411
  function encodeBase64(data) {
381
412
  const base64 = btoa(String.fromCharCode(...data));
package/dist/index.js CHANGED
@@ -255,10 +255,10 @@ async function buildRawMessage(message) {
255
255
  const hasHtml = "html" in message.content;
256
256
  const hasText = "text" in message.content;
257
257
  const isMultipart = hasAttachments || hasHtml && hasText;
258
- lines.push(`From: ${formatAddress(message.sender)}`);
259
- lines.push(`To: ${message.recipients.map(formatAddress).join(", ")}`);
260
- if (message.ccRecipients.length > 0) lines.push(`Cc: ${message.ccRecipients.map(formatAddress).join(", ")}`);
261
- if (message.replyRecipients.length > 0) lines.push(`Reply-To: ${message.replyRecipients.map(formatAddress).join(", ")}`);
258
+ lines.push(`From: ${encodeHeaderValue(formatAddress(message.sender))}`);
259
+ lines.push(`To: ${encodeHeaderValue(message.recipients.map(formatAddress).join(", "))}`);
260
+ if (message.ccRecipients.length > 0) lines.push(`Cc: ${encodeHeaderValue(message.ccRecipients.map(formatAddress).join(", "))}`);
261
+ if (message.replyRecipients.length > 0) lines.push(`Reply-To: ${encodeHeaderValue(message.replyRecipients.map(formatAddress).join(", "))}`);
262
262
  lines.push(`Subject: ${encodeHeaderValue(message.subject)}`);
263
263
  lines.push(`Date: ${(/* @__PURE__ */ new Date()).toUTCString()}`);
264
264
  lines.push(`Message-ID: <${generateMessageId()}>`);
@@ -342,17 +342,48 @@ function encodeHeaderValue(value) {
342
342
  if (!/^[\x20-\x7E]*$/.test(value)) {
343
343
  const utf8Bytes = new TextEncoder().encode(value);
344
344
  const base64 = btoa(String.fromCharCode(...utf8Bytes));
345
- return `=?UTF-8?B?${base64}?=`;
345
+ const maxEncodedLength = 75;
346
+ const encodedWord = `=?UTF-8?B?${base64}?=`;
347
+ if (encodedWord.length <= maxEncodedLength) return encodedWord;
348
+ const words = [];
349
+ let currentBase64 = "";
350
+ for (let i = 0; i < base64.length; i += 4) {
351
+ const chunk = base64.slice(i, i + 4);
352
+ const testWord = `=?UTF-8?B?${currentBase64}${chunk}?=`;
353
+ if (testWord.length <= maxEncodedLength) currentBase64 += chunk;
354
+ else {
355
+ if (currentBase64) words.push(`=?UTF-8?B?${currentBase64}?=`);
356
+ currentBase64 = chunk;
357
+ }
358
+ }
359
+ if (currentBase64) words.push(`=?UTF-8?B?${currentBase64}?=`);
360
+ return words.join(" ");
346
361
  }
347
362
  return value;
348
363
  }
349
364
  function encodeQuotedPrintable(text) {
350
- return text.replace(/[^\x20-\x7E]/g, (char) => {
351
- const code = char.charCodeAt(0);
352
- if (code < 256) return `=${code.toString(16).toUpperCase().padStart(2, "0")}`;
353
- const utf8 = new TextEncoder().encode(char);
354
- return Array.from(utf8).map((byte) => `=${byte.toString(16).toUpperCase().padStart(2, "0")}`).join("");
355
- }).replace(/=$/gm, "=3D").replace(/^\./, "=2E");
365
+ const utf8Bytes = new TextEncoder().encode(text);
366
+ let result = "";
367
+ let lineLength = 0;
368
+ const maxLineLength = 76;
369
+ for (let i = 0; i < utf8Bytes.length; i++) {
370
+ const byte = utf8Bytes[i];
371
+ let encoded = "";
372
+ if (byte < 32 || byte > 126 || byte === 61 || byte === 46 && lineLength === 0) encoded = `=${byte.toString(16).toUpperCase().padStart(2, "0")}`;
373
+ else encoded = String.fromCharCode(byte);
374
+ if (lineLength + encoded.length > maxLineLength) {
375
+ result += "=\r\n";
376
+ lineLength = 0;
377
+ }
378
+ result += encoded;
379
+ lineLength += encoded.length;
380
+ if (byte === 13 && i + 1 < utf8Bytes.length && utf8Bytes[i + 1] === 10) {
381
+ i++;
382
+ result += String.fromCharCode(10);
383
+ lineLength = 0;
384
+ } else if (byte === 10 && (i === 0 || utf8Bytes[i - 1] !== 13)) lineLength = 0;
385
+ }
386
+ return result;
356
387
  }
357
388
  function encodeBase64(data) {
358
389
  const base64 = btoa(String.fromCharCode(...data));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@upyo/smtp",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "SMTP transport for Upyo email library",
5
5
  "keywords": [
6
6
  "email",
@@ -53,7 +53,7 @@
53
53
  },
54
54
  "sideEffects": false,
55
55
  "peerDependencies": {
56
- "@upyo/core": "0.1.0"
56
+ "@upyo/core": "0.1.1"
57
57
  },
58
58
  "devDependencies": {
59
59
  "@dotenvx/dotenvx": "^1.47.3",