@bobfrankston/rmfmail 1.2.252 → 1.2.253

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 (49) hide show
  1. package/client/android-bootstrap.bundle.js +27 -30
  2. package/client/android-bootstrap.bundle.js.map +2 -2
  3. package/client/app.bundle.js +23 -0
  4. package/client/app.bundle.js.map +3 -3
  5. package/client/app.js +26 -0
  6. package/client/app.js.map +1 -1
  7. package/client/app.ts +24 -0
  8. package/client/compose/compose.bundle.js +37 -3
  9. package/client/compose/compose.bundle.js.map +3 -3
  10. package/client/compose/compose.css +13 -0
  11. package/client/compose/compose.js +41 -1
  12. package/client/compose/compose.js.map +1 -1
  13. package/client/compose/compose.ts +38 -1
  14. package/client/lib/api-client.js +7 -0
  15. package/client/lib/api-client.js.map +1 -1
  16. package/client/lib/api-client.ts +8 -0
  17. package/client/lib/mailxapi.js +10 -2
  18. package/package.json +3 -3
  19. package/packages/mailx-imap/package-lock.json +2 -2
  20. package/packages/mailx-imap/package.json +1 -1
  21. package/packages/mailx-service/index.d.ts +19 -1
  22. package/packages/mailx-service/index.d.ts.map +1 -1
  23. package/packages/mailx-service/index.js +27 -2
  24. package/packages/mailx-service/index.js.map +1 -1
  25. package/packages/mailx-service/index.ts +28 -2
  26. package/packages/mailx-service/jsonrpc.js +3 -1
  27. package/packages/mailx-service/jsonrpc.js.map +1 -1
  28. package/packages/mailx-service/jsonrpc.ts +3 -1
  29. package/packages/mailx-service/package.json +1 -1
  30. package/packages/mailx-settings/package.json +1 -1
  31. package/packages/mailx-store/package.json +1 -1
  32. package/packages/mailx-store-web/android-bootstrap.js +1 -1
  33. package/packages/mailx-store-web/android-bootstrap.js.map +1 -1
  34. package/packages/mailx-store-web/android-bootstrap.ts +1 -1
  35. package/packages/mailx-store-web/package.json +1 -1
  36. package/packages/mailx-store-web/web-service.d.ts +5 -1
  37. package/packages/mailx-store-web/web-service.d.ts.map +1 -1
  38. package/packages/mailx-store-web/web-service.js +5 -1
  39. package/packages/mailx-store-web/web-service.js.map +1 -1
  40. package/packages/mailx-store-web/web-service.ts +5 -1
  41. package/packages/mailx-types/mailx-api.d.ts +17 -1
  42. package/packages/mailx-types/mailx-api.d.ts.map +1 -1
  43. package/packages/mailx-types/mailx-api.ts +10 -1
  44. package/packages/mailx-types/mime-build.d.ts.map +1 -1
  45. package/packages/mailx-types/mime-build.js +35 -30
  46. package/packages/mailx-types/mime-build.js.map +1 -1
  47. package/packages/mailx-types/mime-build.ts +44 -32
  48. package/packages/mailx-types/package.json +1 -1
  49. /package/packages/mailx-imap/{node_modules.npmglobalize-stash-85680 → node_modules.npmglobalize-stash-54924}/.package-lock.json +0 -0
@@ -116,14 +116,18 @@ export function buildMimeMessage(opts: BuildMimeOptions): BuiltMime {
116
116
  ];
117
117
  const commonHeaders = headers.filter((h): h is string => h !== null);
118
118
 
119
- // ── draft: a single text/html part and nothing else ──
119
+ // ── draft: a single text/html part, plus attachments if there are any ──
120
+ // "Simple" means no alternative and no inline-image extraction — it does
121
+ // NOT mean "drop the user's files". This branch used to `return` before
122
+ // ever reading opts.attachments, so a draft saved with a 65KB image was
123
+ // written body-only and the file was gone on reopen (Bob 2026-08-11).
120
124
  if (opts.simpleHtml) {
121
125
  const body = opts.bodyHtml || opts.bodyText || "";
122
- const raw = [
123
- ...commonHeaders,
124
- `Content-Type: text/html; charset=UTF-8`,
125
- `Content-Transfer-Encoding: quoted-printable`,
126
- ].join("\r\n") + `\r\n\r\n` + encodeQuotedPrintable(body);
126
+ const htmlPart = {
127
+ headers: [`Content-Type: text/html; charset=UTF-8`, `Content-Transfer-Encoding: quoted-printable`],
128
+ body: encodeQuotedPrintable(body),
129
+ };
130
+ const raw = wrapWithAttachments(commonHeaders, htmlPart, opts.attachments || [], newBoundary);
127
131
  return { raw, messageId, inlineImageCount: 0 };
128
132
  }
129
133
 
@@ -186,32 +190,40 @@ export function buildMimeMessage(opts: BuildMimeOptions): BuiltMime {
186
190
  };
187
191
  };
188
192
 
189
- const attachments = opts.attachments || [];
190
- let raw: string;
191
- if (attachments.length > 0) {
192
- const mixedBoundary = newBoundary();
193
- const inner = makeInner();
194
- const parts: string[] = [
195
- `--${mixedBoundary}\r\n` + inner.headers.join("\r\n") + `\r\n\r\n` + inner.body,
196
- ];
197
- for (const att of attachments) {
198
- // A quote or newline in a filename would break out of the header.
199
- const filename = (att.filename || "attachment").replace(/[\r\n"]/g, "_");
200
- const mime = att.mimeType || "application/octet-stream";
201
- parts.push(
202
- `--${mixedBoundary}\r\n` +
203
- `Content-Type: ${mime}; name="${filename}"\r\n` +
204
- `Content-Disposition: attachment; filename="${filename}"\r\n` +
205
- `Content-Transfer-Encoding: base64\r\n\r\n` +
206
- `${wrap76(att.dataBase64 || "")}\r\n`
207
- );
208
- }
209
- raw = [...commonHeaders, `Content-Type: multipart/mixed; boundary="${mixedBoundary}"`].join("\r\n")
210
- + `\r\n\r\n` + parts.join("") + `--${mixedBoundary}--\r\n`;
211
- } else {
212
- const inner = makeInner();
213
- raw = [...commonHeaders, ...inner.headers].join("\r\n") + `\r\n\r\n` + inner.body;
214
- }
193
+ const raw = wrapWithAttachments(commonHeaders, makeInner(), opts.attachments || [], newBoundary);
215
194
 
216
195
  return { raw, messageId, inlineImageCount: inlineImages.length };
217
196
  }
197
+
198
+ /** Emit `inner` as the whole body, or — when there are attachments — as the
199
+ * first part of a multipart/mixed followed by one base64 part per file.
200
+ * Shared by the draft and send paths so a file can never be dropped by one
201
+ * of them alone. */
202
+ function wrapWithAttachments(
203
+ commonHeaders: string[],
204
+ inner: { headers: string[]; body: string },
205
+ attachments: MimeAttachment[],
206
+ newBoundary: () => string,
207
+ ): string {
208
+ if (attachments.length === 0) {
209
+ return [...commonHeaders, ...inner.headers].join("\r\n") + `\r\n\r\n` + inner.body;
210
+ }
211
+ const mixedBoundary = newBoundary();
212
+ const parts: string[] = [
213
+ `--${mixedBoundary}\r\n` + inner.headers.join("\r\n") + `\r\n\r\n` + inner.body,
214
+ ];
215
+ for (const att of attachments) {
216
+ // A quote or newline in a filename would break out of the header.
217
+ const filename = (att.filename || "attachment").replace(/[\r\n"]/g, "_");
218
+ const mime = att.mimeType || "application/octet-stream";
219
+ parts.push(
220
+ `--${mixedBoundary}\r\n` +
221
+ `Content-Type: ${mime}; name="${filename}"\r\n` +
222
+ `Content-Disposition: attachment; filename="${filename}"\r\n` +
223
+ `Content-Transfer-Encoding: base64\r\n\r\n` +
224
+ `${wrap76(att.dataBase64 || "")}\r\n`
225
+ );
226
+ }
227
+ return [...commonHeaders, `Content-Type: multipart/mixed; boundary="${mixedBoundary}"`].join("\r\n")
228
+ + `\r\n\r\n` + parts.join("") + `--${mixedBoundary}--\r\n`;
229
+ }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bobfrankston/mailx-types",
3
- "version": "0.1.45",
3
+ "version": "0.1.47",
4
4
  "type": "module",
5
5
  "main": "index.js",
6
6
  "types": "index.d.ts",