@bobfrankston/mailx-types 0.1.43 → 0.1.47
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/mailx-api.d.ts +24 -1
- package/mime-build.js +35 -30
- package/package.json +1 -1
package/mailx-api.d.ts
CHANGED
|
@@ -78,10 +78,26 @@ export interface MailxApi {
|
|
|
78
78
|
moveFolderToTrash(accountId: string, folderId: number): Promise<void>;
|
|
79
79
|
emptyFolder(accountId: string, folderId: number): Promise<void>;
|
|
80
80
|
sendMessage(msg: any): Promise<void>;
|
|
81
|
-
|
|
81
|
+
/** Positional here because both service classes implement it that way;
|
|
82
|
+
* the bridge is what turns the client's single params object into these
|
|
83
|
+
* arguments. Keep the tail in sync with jsonrpc's `case "saveDraft"` —
|
|
84
|
+
* this list had already fallen behind by one (`from`), and `attachments`
|
|
85
|
+
* made it two (2026-08-11). A param missing here is a param silently
|
|
86
|
+
* dropped on the way to the daemon. */
|
|
87
|
+
saveDraft(accountId: string, subject: string, bodyHtml: string, bodyText: string, to?: string, cc?: string, previousDraftUid?: number, draftId?: string, bcc?: string, from?: string, attachments?: {
|
|
88
|
+
filename: string;
|
|
89
|
+
mimeType: string;
|
|
90
|
+
dataBase64: string;
|
|
91
|
+
}[]): Promise<{
|
|
82
92
|
draftUid: number | null;
|
|
83
93
|
draftId: string;
|
|
84
94
|
}>;
|
|
95
|
+
/** Open a compose-window attachment in the OS default app. Optional:
|
|
96
|
+
* desktop-only (Android has no staging dir to launch from). */
|
|
97
|
+
openPendingAttachment?(filename: string, mimeType: string, dataBase64: string): Promise<{
|
|
98
|
+
ok: boolean;
|
|
99
|
+
path: string;
|
|
100
|
+
}>;
|
|
85
101
|
deleteDraft(accountId: string, draftUid: number, draftId?: string): Promise<void>;
|
|
86
102
|
getOutboxStatus(): any | Promise<any>;
|
|
87
103
|
listQueuedOutgoing(): any[] | Promise<any[]>;
|
|
@@ -204,6 +220,13 @@ export interface MailxApi {
|
|
|
204
220
|
status: number;
|
|
205
221
|
statusText: string;
|
|
206
222
|
}>;
|
|
223
|
+
/** Fetch a remote image's bytes for the viewer's "Copy image" action —
|
|
224
|
+
* cross-origin images can't be read inside the WebView (no CORS headers,
|
|
225
|
+
* tainted canvas), so the daemon does it. Optional: desktop-only. */
|
|
226
|
+
fetchRemoteImage?(url: string): Promise<{
|
|
227
|
+
base64: string;
|
|
228
|
+
contentType: string;
|
|
229
|
+
}>;
|
|
207
230
|
consumePendingMailto(): any | Promise<any>;
|
|
208
231
|
/** C46 Windows Share target — startup-race twin of the daemon's
|
|
209
232
|
* fs.watch on pending-share.json. Optional: desktop-only. */
|
package/mime-build.js
CHANGED
|
@@ -64,14 +64,18 @@ export function buildMimeMessage(opts) {
|
|
|
64
64
|
`MIME-Version: 1.0`,
|
|
65
65
|
];
|
|
66
66
|
const commonHeaders = headers.filter((h) => h !== null);
|
|
67
|
-
// ── draft: a single text/html part
|
|
67
|
+
// ── draft: a single text/html part, plus attachments if there are any ──
|
|
68
|
+
// "Simple" means no alternative and no inline-image extraction — it does
|
|
69
|
+
// NOT mean "drop the user's files". This branch used to `return` before
|
|
70
|
+
// ever reading opts.attachments, so a draft saved with a 65KB image was
|
|
71
|
+
// written body-only and the file was gone on reopen (Bob 2026-08-11).
|
|
68
72
|
if (opts.simpleHtml) {
|
|
69
73
|
const body = opts.bodyHtml || opts.bodyText || "";
|
|
70
|
-
const
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
74
|
+
const htmlPart = {
|
|
75
|
+
headers: [`Content-Type: text/html; charset=UTF-8`, `Content-Transfer-Encoding: quoted-printable`],
|
|
76
|
+
body: encodeQuotedPrintable(body),
|
|
77
|
+
};
|
|
78
|
+
const raw = wrapWithAttachments(commonHeaders, htmlPart, opts.attachments || [], newBoundary);
|
|
75
79
|
return { raw, messageId, inlineImageCount: 0 };
|
|
76
80
|
}
|
|
77
81
|
// Pasted images live in the editor HTML as data: URIs — fine in a WebView,
|
|
@@ -130,31 +134,32 @@ export function buildMimeMessage(opts) {
|
|
|
130
134
|
body: textEncoded,
|
|
131
135
|
};
|
|
132
136
|
};
|
|
133
|
-
const
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
const filename = (att.filename || "attachment").replace(/[\r\n"]/g, "_");
|
|
144
|
-
const mime = att.mimeType || "application/octet-stream";
|
|
145
|
-
parts.push(`--${mixedBoundary}\r\n` +
|
|
146
|
-
`Content-Type: ${mime}; name="${filename}"\r\n` +
|
|
147
|
-
`Content-Disposition: attachment; filename="${filename}"\r\n` +
|
|
148
|
-
`Content-Transfer-Encoding: base64\r\n\r\n` +
|
|
149
|
-
`${wrap76(att.dataBase64 || "")}\r\n`);
|
|
150
|
-
}
|
|
151
|
-
raw = [...commonHeaders, `Content-Type: multipart/mixed; boundary="${mixedBoundary}"`].join("\r\n")
|
|
152
|
-
+ `\r\n\r\n` + parts.join("") + `--${mixedBoundary}--\r\n`;
|
|
137
|
+
const raw = wrapWithAttachments(commonHeaders, makeInner(), opts.attachments || [], newBoundary);
|
|
138
|
+
return { raw, messageId, inlineImageCount: inlineImages.length };
|
|
139
|
+
}
|
|
140
|
+
/** Emit `inner` as the whole body, or — when there are attachments — as the
|
|
141
|
+
* first part of a multipart/mixed followed by one base64 part per file.
|
|
142
|
+
* Shared by the draft and send paths so a file can never be dropped by one
|
|
143
|
+
* of them alone. */
|
|
144
|
+
function wrapWithAttachments(commonHeaders, inner, attachments, newBoundary) {
|
|
145
|
+
if (attachments.length === 0) {
|
|
146
|
+
return [...commonHeaders, ...inner.headers].join("\r\n") + `\r\n\r\n` + inner.body;
|
|
153
147
|
}
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
148
|
+
const mixedBoundary = newBoundary();
|
|
149
|
+
const parts = [
|
|
150
|
+
`--${mixedBoundary}\r\n` + inner.headers.join("\r\n") + `\r\n\r\n` + inner.body,
|
|
151
|
+
];
|
|
152
|
+
for (const att of attachments) {
|
|
153
|
+
// A quote or newline in a filename would break out of the header.
|
|
154
|
+
const filename = (att.filename || "attachment").replace(/[\r\n"]/g, "_");
|
|
155
|
+
const mime = att.mimeType || "application/octet-stream";
|
|
156
|
+
parts.push(`--${mixedBoundary}\r\n` +
|
|
157
|
+
`Content-Type: ${mime}; name="${filename}"\r\n` +
|
|
158
|
+
`Content-Disposition: attachment; filename="${filename}"\r\n` +
|
|
159
|
+
`Content-Transfer-Encoding: base64\r\n\r\n` +
|
|
160
|
+
`${wrap76(att.dataBase64 || "")}\r\n`);
|
|
157
161
|
}
|
|
158
|
-
return
|
|
162
|
+
return [...commonHeaders, `Content-Type: multipart/mixed; boundary="${mixedBoundary}"`].join("\r\n")
|
|
163
|
+
+ `\r\n\r\n` + parts.join("") + `--${mixedBoundary}--\r\n`;
|
|
159
164
|
}
|
|
160
165
|
//# sourceMappingURL=mime-build.js.map
|