@dawnlit/outlook-bridge 1.1.1 → 2.1.0
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 +152 -183
- package/dist/backend.d.ts +151 -0
- package/dist/backend.js +3 -0
- package/dist/backend.js.map +1 -0
- package/dist/bridge.d.ts +56 -0
- package/dist/bridge.js +507 -0
- package/dist/bridge.js.map +1 -0
- package/dist/errors.d.ts +79 -43
- package/dist/errors.js +93 -52
- package/dist/errors.js.map +1 -1
- package/dist/index.d.ts +10 -10
- package/dist/index.js +25 -52
- package/dist/index.js.map +1 -1
- package/dist/mac/accounts.d.ts +7 -2
- package/dist/mac/accounts.js +23 -6
- package/dist/mac/accounts.js.map +1 -1
- package/dist/mac/attachments.d.ts +5 -25
- package/dist/mac/attachments.js +40 -99
- package/dist/mac/attachments.js.map +1 -1
- package/dist/mac/bounces.d.ts +12 -27
- package/dist/mac/bounces.js +96 -117
- package/dist/mac/bounces.js.map +1 -1
- package/dist/mac/cleanup.d.ts +11 -25
- package/dist/mac/cleanup.js +44 -107
- package/dist/mac/cleanup.js.map +1 -1
- package/dist/mac/drafts.d.ts +15 -34
- package/dist/mac/drafts.js +113 -186
- package/dist/mac/drafts.js.map +1 -1
- package/dist/mac/folders.d.ts +12 -21
- package/dist/mac/folders.js +44 -59
- package/dist/mac/folders.js.map +1 -1
- package/dist/mac/index.d.ts +2 -22
- package/dist/mac/index.js +19 -81
- package/dist/mac/index.js.map +1 -1
- package/dist/mac/profile.d.ts +13 -0
- package/dist/mac/profile.js +149 -0
- package/dist/mac/profile.js.map +1 -0
- package/dist/mac/read.d.ts +21 -37
- package/dist/mac/read.js +198 -216
- package/dist/mac/read.js.map +1 -1
- package/dist/mac/run.d.ts +51 -38
- package/dist/mac/run.js +138 -103
- package/dist/mac/run.js.map +1 -1
- package/dist/mac/scripts.d.ts +90 -91
- package/dist/mac/scripts.js +167 -126
- package/dist/mac/scripts.js.map +1 -1
- package/dist/mac/send.d.ts +10 -20
- package/dist/mac/send.js +99 -89
- package/dist/mac/send.js.map +1 -1
- package/dist/mac/signatures.d.ts +2 -2
- package/dist/mac/signatures.js +9 -11
- package/dist/mac/signatures.js.map +1 -1
- package/dist/mac/templates.d.ts +14 -26
- package/dist/mac/templates.js +79 -120
- package/dist/mac/templates.js.map +1 -1
- package/dist/mail.d.ts +80 -46
- package/dist/mail.js +179 -73
- package/dist/mail.js.map +1 -1
- package/dist/runtime.d.ts +76 -43
- package/dist/runtime.js +103 -48
- package/dist/runtime.js.map +1 -1
- package/dist/shared/args.d.ts +48 -0
- package/dist/shared/args.js +216 -0
- package/dist/shared/args.js.map +1 -0
- package/dist/shared/attachmentMatch.d.ts +26 -20
- package/dist/shared/attachmentMatch.js +60 -23
- package/dist/shared/attachmentMatch.js.map +1 -1
- package/dist/shared/bounceReport.d.ts +11 -0
- package/dist/shared/bounceReport.js +45 -0
- package/dist/shared/bounceReport.js.map +1 -0
- package/dist/shared/bounceRules.d.ts +38 -10
- package/dist/shared/bounceRules.js +50 -32
- package/dist/shared/bounceRules.js.map +1 -1
- package/dist/shared/exec.d.ts +21 -0
- package/dist/shared/exec.js +47 -0
- package/dist/shared/exec.js.map +1 -0
- package/dist/shared/json.d.ts +17 -6
- package/dist/shared/json.js +35 -24
- package/dist/shared/json.js.map +1 -1
- package/dist/shared/locate.d.ts +31 -0
- package/dist/shared/locate.js +128 -0
- package/dist/shared/locate.js.map +1 -0
- package/dist/shared/replyBody.d.ts +31 -36
- package/dist/shared/replyBody.js +83 -72
- package/dist/shared/replyBody.js.map +1 -1
- package/dist/shared/templateLimits.d.ts +14 -0
- package/dist/shared/templateLimits.js +21 -0
- package/dist/shared/templateLimits.js.map +1 -0
- package/dist/templateBody.d.ts +69 -0
- package/dist/{outlookTemplateSections.js → templateBody.js} +80 -73
- package/dist/templateBody.js.map +1 -0
- package/dist/types.d.ts +381 -168
- package/dist/types.js +8 -9
- package/dist/types.js.map +1 -1
- package/dist/windows/accounts.d.ts +7 -12
- package/dist/windows/accounts.js +19 -34
- package/dist/windows/accounts.js.map +1 -1
- package/dist/windows/attachments.d.ts +9 -24
- package/dist/windows/attachments.js +94 -119
- package/dist/windows/attachments.js.map +1 -1
- package/dist/windows/bounces.d.ts +13 -26
- package/dist/windows/bounces.js +168 -200
- package/dist/windows/bounces.js.map +1 -1
- package/dist/windows/cleanup.d.ts +23 -23
- package/dist/windows/cleanup.js +99 -113
- package/dist/windows/cleanup.js.map +1 -1
- package/dist/windows/drafts.d.ts +16 -35
- package/dist/windows/drafts.js +156 -253
- package/dist/windows/drafts.js.map +1 -1
- package/dist/windows/folders.d.ts +8 -13
- package/dist/windows/folders.js +44 -52
- package/dist/windows/folders.js.map +1 -1
- package/dist/windows/index.d.ts +2 -27
- package/dist/windows/index.js +11 -66
- package/dist/windows/index.js.map +1 -1
- package/dist/windows/read.d.ts +24 -60
- package/dist/windows/read.js +265 -306
- package/dist/windows/read.js.map +1 -1
- package/dist/windows/run.d.ts +38 -40
- package/dist/windows/run.js +151 -112
- package/dist/windows/run.js.map +1 -1
- package/dist/windows/scripts.d.ts +65 -55
- package/dist/windows/scripts.js +109 -85
- package/dist/windows/scripts.js.map +1 -1
- package/dist/windows/send.d.ts +9 -9
- package/dist/windows/send.js +76 -88
- package/dist/windows/send.js.map +1 -1
- package/dist/windows/signatures.d.ts +4 -10
- package/dist/windows/signatures.js +30 -37
- package/dist/windows/signatures.js.map +1 -1
- package/dist/windows/templates.d.ts +12 -18
- package/dist/windows/templates.js +117 -150
- package/dist/windows/templates.js.map +1 -1
- package/package.json +5 -4
- package/dist/OutlookService.d.ts +0 -43
- package/dist/OutlookService.js +0 -137
- package/dist/OutlookService.js.map +0 -1
- package/dist/outlookTemplateSections.d.ts +0 -62
- package/dist/outlookTemplateSections.js.map +0 -1
- package/dist/shared/capabilities.d.ts +0 -3
- package/dist/shared/capabilities.js +0 -9
- package/dist/shared/capabilities.js.map +0 -1
package/dist/mail.js
CHANGED
|
@@ -1,103 +1,209 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.PROTECTED_MAIL_REASON = exports.WELL_KNOWN_FOLDERS = void 0;
|
|
4
|
-
exports.
|
|
3
|
+
exports.REPLY_PREFIX = exports.REPLY_PREFIX_SOURCE = exports.NON_INCOMING_ROOTS = exports.PROTECTED_MAIL_REASON = exports.WELL_KNOWN_FOLDERS = exports.FolderId = void 0;
|
|
4
|
+
exports.deleteStatus = deleteStatus;
|
|
5
|
+
exports.deleteMailResult = deleteMailResult;
|
|
5
6
|
exports.isOutgoingRoot = isOutgoingRoot;
|
|
7
|
+
exports.hasReplyPrefix = hasReplyPrefix;
|
|
8
|
+
exports.threadSubject = threadSubject;
|
|
9
|
+
exports.subjectGlobSource = subjectGlobSource;
|
|
10
|
+
exports.folderLeafName = folderLeafName;
|
|
6
11
|
exports.mailFolderRef = mailFolderRef;
|
|
12
|
+
exports.folderOption = folderOption;
|
|
7
13
|
exports.splitQuotedOriginal = splitQuotedOriginal;
|
|
14
|
+
// Platform-neutral mail helpers: folder strings, reply prefixes, splitting a
|
|
15
|
+
// reply from the thread it quotes, and what deleting mail reports. Pure
|
|
16
|
+
// functions, usable without Outlook, and shared by both platforms so neither can
|
|
17
|
+
// come to read these differently.
|
|
18
|
+
const errors_1 = require("./errors");
|
|
8
19
|
/**
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
20
|
+
* Outlook's olDefaultFolders ids for the folders this package addresses by
|
|
21
|
+
* role rather than by name. The ids are Outlook's own, and a folder's NAME is
|
|
22
|
+
* localized, so a role is always resolved through its id.
|
|
23
|
+
*/
|
|
24
|
+
exports.FolderId = Object.freeze({
|
|
25
|
+
DeletedItems: 3,
|
|
26
|
+
Outbox: 4,
|
|
27
|
+
SentMail: 5,
|
|
28
|
+
Inbox: 6,
|
|
29
|
+
Drafts: 16,
|
|
30
|
+
Junk: 23,
|
|
31
|
+
});
|
|
32
|
+
/**
|
|
33
|
+
* The folders addressable by name rather than by walking from the Inbox, with
|
|
34
|
+
* their olDefaultFolders id. A well-known name wins over a user folder of the
|
|
35
|
+
* same name; reach the latter by qualifying it (`Inbox\Drafts`).
|
|
36
|
+
*/
|
|
37
|
+
exports.WELL_KNOWN_FOLDERS = Object.freeze({
|
|
38
|
+
'inbox': exports.FolderId.Inbox,
|
|
39
|
+
'sent items': exports.FolderId.SentMail,
|
|
40
|
+
'sent': exports.FolderId.SentMail,
|
|
41
|
+
'sent mail': exports.FolderId.SentMail,
|
|
42
|
+
'drafts': exports.FolderId.Drafts,
|
|
43
|
+
'deleted items': exports.FolderId.DeletedItems,
|
|
44
|
+
'deleted': exports.FolderId.DeletedItems,
|
|
45
|
+
'trash': exports.FolderId.DeletedItems,
|
|
46
|
+
'junk email': exports.FolderId.Junk,
|
|
47
|
+
'junk': exports.FolderId.Junk,
|
|
48
|
+
'outbox': exports.FolderId.Outbox,
|
|
49
|
+
});
|
|
50
|
+
/** The Inbox root, as a folder reference. */
|
|
51
|
+
const INBOX_REF = Object.freeze({
|
|
52
|
+
rootId: exports.FolderId.Inbox,
|
|
53
|
+
rootLabel: 'Inbox',
|
|
54
|
+
segments: [],
|
|
55
|
+
});
|
|
56
|
+
/**
|
|
57
|
+
* Why `deleteOutlookEmails` refuses an item, worded once for both platforms —
|
|
58
|
+
* it reaches the caller verbatim as a per-item `reason`.
|
|
15
59
|
*/
|
|
16
|
-
|
|
17
|
-
|
|
60
|
+
exports.PROTECTED_MAIL_REASON = 'received or sent mail (Inbox/Sent Items or a subfolder) - pass allowProtected to override';
|
|
61
|
+
const DELETE_STATUSES = ['deleted', 'would-delete', 'refused', 'failed'];
|
|
62
|
+
/** The status a delete script reported for one item; anything it shouldn't have said is a failure. */
|
|
63
|
+
function deleteStatus(reported) {
|
|
64
|
+
return DELETE_STATUSES.find(status => status === reported) ?? 'failed';
|
|
65
|
+
}
|
|
66
|
+
/** A deleteOutlookEmails result: every outcome, and how many ended each way. */
|
|
67
|
+
function deleteMailResult(dryRun, items) {
|
|
68
|
+
const tally = (status) => items.filter(item => item.status === status).length;
|
|
69
|
+
return { dryRun, deleted: tally('deleted'), refused: tally('refused'), failed: tally('failed'), items };
|
|
18
70
|
}
|
|
19
71
|
/**
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
|
|
23
|
-
|
|
72
|
+
* Roots whose items are outgoing mail, which carries a sent time where incoming
|
|
73
|
+
* mail carries a received one — so a date filter has to know which it's on.
|
|
74
|
+
*/
|
|
75
|
+
function isOutgoingRoot(rootId) {
|
|
76
|
+
return rootId === exports.FolderId.SentMail || rootId === exports.FolderId.Outbox || rootId === exports.FolderId.Drafts;
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Roots an Inbox walk must never descend into.
|
|
24
80
|
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
81
|
+
* On an Exchange profile these sit beside the Inbox and a walk from it can't
|
|
82
|
+
* reach them anyway. On an IMAP profile Outlook nests them UNDER the Inbox, so
|
|
83
|
+
* the same walk would hand back mail already sent, drafted or thrown away as if
|
|
84
|
+
* it had just arrived. They are matched by id, never by name: the names are
|
|
85
|
+
* localized.
|
|
27
86
|
*/
|
|
28
|
-
exports.
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
deleted: 3,
|
|
36
|
-
trash: 3,
|
|
37
|
-
'junk email': 23,
|
|
38
|
-
junk: 23,
|
|
39
|
-
outbox: 4,
|
|
40
|
-
};
|
|
87
|
+
exports.NON_INCOMING_ROOTS = Object.freeze([
|
|
88
|
+
exports.FolderId.DeletedItems,
|
|
89
|
+
exports.FolderId.Outbox,
|
|
90
|
+
exports.FolderId.SentMail,
|
|
91
|
+
exports.FolderId.Drafts,
|
|
92
|
+
exports.FolderId.Junk,
|
|
93
|
+
]);
|
|
41
94
|
/**
|
|
42
|
-
*
|
|
95
|
+
* Subject prefixes that mark a reply or a forward.
|
|
96
|
+
*
|
|
97
|
+
* Kept as a pattern SOURCE because it runs in two engines: macOS tests it as a
|
|
98
|
+
* JavaScript RegExp, Windows inside PowerShell's .NET one, and both read every
|
|
99
|
+
* construct used here the same way. Non-ASCII is written as \uXXXX so the
|
|
100
|
+
* generated PowerShell stays plain ASCII.
|
|
43
101
|
*
|
|
44
|
-
*
|
|
45
|
-
*
|
|
46
|
-
*
|
|
47
|
-
*
|
|
48
|
-
*
|
|
49
|
-
* package exposes.
|
|
102
|
+
* Not only English: mail threaded in Chinese, Japanese or Korean replies under
|
|
103
|
+
* 回复:/返信:/답장: and forwards under 转发:/転送:/전달:, and the European
|
|
104
|
+
* clients use AW:, WG:, SV:, TR:, RV:, ENC: and friends. The colon may be the
|
|
105
|
+
* full-width one a CJK input method types, and Outlook's reply counter (RE[2]:)
|
|
106
|
+
* can sit before it.
|
|
50
107
|
*/
|
|
51
|
-
|
|
108
|
+
const REPLY_PREFIXES = [
|
|
109
|
+
're', 'fwd?', 'aw', 'wg', 'sv', 'vs', 'vb', 'tr', 'res', 'rv', 'enc', 'odp',
|
|
110
|
+
'\\u56DE\\u590D', '\\u56DE\\u8986', '\\u7B54\\u590D', '\\u7B54\\u8986',
|
|
111
|
+
'\\u8F6C\\u53D1', '\\u8F49\\u767C', '\\u8FD4\\u4FE1', '\\u8EE2\\u9001',
|
|
112
|
+
'\\uB2F5\\uC7A5', '\\uC804\\uB2EC',
|
|
113
|
+
];
|
|
114
|
+
/** The reply/forward prefixes as one case-insensitive pattern source. */
|
|
115
|
+
exports.REPLY_PREFIX_SOURCE = '^\\s*(?:' + REPLY_PREFIXES.join('|') + ')\\s*(?:\\[\\d+\\])?\\s*[:\\uFF1A]';
|
|
116
|
+
exports.REPLY_PREFIX = new RegExp(exports.REPLY_PREFIX_SOURCE, 'i');
|
|
52
117
|
/**
|
|
53
|
-
*
|
|
118
|
+
* Whether a subject opens on a reply or forward prefix.
|
|
54
119
|
*
|
|
55
|
-
*
|
|
56
|
-
*
|
|
57
|
-
* which is why it lives beside `WELL_KNOWN_FOLDERS` rather than as raw
|
|
58
|
-
* `olDefaultFolders` ids compared inline in each reader.
|
|
120
|
+
* This reads what the subject claims, not how the email was made: a forward
|
|
121
|
+
* carries a prefix too, and a client can leave one off a real reply.
|
|
59
122
|
*/
|
|
60
|
-
function
|
|
61
|
-
return
|
|
62
|
-
|| rootId === exports.WELL_KNOWN_FOLDERS.outbox
|
|
63
|
-
|| rootId === exports.WELL_KNOWN_FOLDERS.drafts;
|
|
123
|
+
function hasReplyPrefix(subject) {
|
|
124
|
+
return exports.REPLY_PREFIX.test(subject);
|
|
64
125
|
}
|
|
65
126
|
/**
|
|
66
|
-
*
|
|
67
|
-
*
|
|
68
|
-
*
|
|
69
|
-
*
|
|
70
|
-
|
|
71
|
-
|
|
127
|
+
* The subject every message in a thread shares: every reply and forward prefix
|
|
128
|
+
* stripped, however many clients stacked them (`RE: FW: RE[2]: Rate request`
|
|
129
|
+
* is `Rate request`), and whitespace collapsed. Case is kept, so lower-case
|
|
130
|
+
* both sides to compare two threads.
|
|
131
|
+
*/
|
|
132
|
+
function threadSubject(subject) {
|
|
133
|
+
let rest = subject;
|
|
134
|
+
// Each pass removes at least the prefix's colon, so this always ends.
|
|
135
|
+
while (exports.REPLY_PREFIX.test(rest))
|
|
136
|
+
rest = rest.replace(exports.REPLY_PREFIX, '');
|
|
137
|
+
return rest.replace(/\s+/g, ' ').trim();
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* A subject glob as an anchored regex source: `*` matches any run, `?` one
|
|
141
|
+
* character, everything else itself. Written with constructs JavaScript and
|
|
142
|
+
* .NET read identically, so both platforms apply exactly the same test.
|
|
143
|
+
*/
|
|
144
|
+
function subjectGlobSource(glob) {
|
|
145
|
+
let source = '';
|
|
146
|
+
for (const ch of glob) {
|
|
147
|
+
if (ch === '*')
|
|
148
|
+
source += '.*';
|
|
149
|
+
else if (ch === '?')
|
|
150
|
+
source += '.';
|
|
151
|
+
else
|
|
152
|
+
source += ch.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
153
|
+
}
|
|
154
|
+
return `^${source}$`;
|
|
155
|
+
}
|
|
156
|
+
/** The leaf name of a folder path or bare name. */
|
|
157
|
+
function folderLeafName(entry) {
|
|
158
|
+
return entry.trim().split(/[\\/]/).filter(Boolean).pop() ?? '';
|
|
159
|
+
}
|
|
160
|
+
/** A full Outlook folder path starts with the mailbox: `\\mailbox@example.com\…`. */
|
|
161
|
+
const MAILBOX_PREFIX = /^\\\\[^\\/]+[\\/]/;
|
|
162
|
+
function folderSegments(folder) {
|
|
163
|
+
return folder.trim().replace(MAILBOX_PREFIX, '').split(/[\\/]+/).map(s => s.trim()).filter(Boolean);
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* Resolve a folder string to a well-known root plus the segments below it.
|
|
72
167
|
*
|
|
73
|
-
*
|
|
74
|
-
*
|
|
168
|
+
* Accepts every shape a person has to hand: a bare name ('Invoices'), a
|
|
169
|
+
* relative path ('Invoices\Paid'), a well-known folder ('Sent Items'), a path
|
|
170
|
+
* under one ('Deleted Items\2026'), and the full path listInboxFolders prints
|
|
171
|
+
* ('\\team@example.com\Inbox\Invoices'). Anything not starting at a well-known
|
|
172
|
+
* folder is taken as relative to the Inbox.
|
|
75
173
|
*/
|
|
76
174
|
function mailFolderRef(folder) {
|
|
77
|
-
const
|
|
78
|
-
const
|
|
79
|
-
if (
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
175
|
+
const segments = folderSegments(folder);
|
|
176
|
+
const head = segments[0]?.toLowerCase();
|
|
177
|
+
if (head !== undefined && Object.prototype.hasOwnProperty.call(exports.WELL_KNOWN_FOLDERS, head)) {
|
|
178
|
+
return { rootId: exports.WELL_KNOWN_FOLDERS[head], rootLabel: segments[0], segments: segments.slice(1) };
|
|
179
|
+
}
|
|
180
|
+
return { rootId: exports.FolderId.Inbox, rootLabel: 'Inbox', segments };
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* A caller's folder option as a reference: none given means the Inbox root,
|
|
184
|
+
* and a string that names no folder at all (only separators) is refused rather
|
|
185
|
+
* than silently read as the Inbox root.
|
|
186
|
+
*/
|
|
187
|
+
function folderOption(folder) {
|
|
188
|
+
if (folder === undefined || folder.trim() === '')
|
|
189
|
+
return { ref: INBOX_REF, label: 'Inbox' };
|
|
190
|
+
if (folderSegments(folder).length === 0) {
|
|
191
|
+
throw new errors_1.InvalidRequestError(`Folder '${folder}' does not name a folder.`);
|
|
84
192
|
}
|
|
85
|
-
return {
|
|
193
|
+
return { ref: mailFolderRef(folder), label: folder.trim() };
|
|
86
194
|
}
|
|
87
195
|
/**
|
|
88
196
|
* Split a plain-text reply into the sender's own text and the quoted thread
|
|
89
197
|
* below it.
|
|
90
198
|
*
|
|
91
|
-
* This matters more than it looks: the quoted original
|
|
92
|
-
*
|
|
93
|
-
*
|
|
94
|
-
* worse failure than the truncation this exists to fix.
|
|
199
|
+
* This matters more than it looks: the quoted original carries the text of the
|
|
200
|
+
* message being answered, and handing a reader one blob invites a figure from
|
|
201
|
+
* that message to be read back as the sender's own.
|
|
95
202
|
*
|
|
96
|
-
*
|
|
97
|
-
*
|
|
98
|
-
*
|
|
99
|
-
*
|
|
100
|
-
* outcome worth avoiding.
|
|
203
|
+
* Splits at the earliest separator Outlook or the common webmail clients emit.
|
|
204
|
+
* If that would leave no new text at all — a bottom-posted reply, or a body
|
|
205
|
+
* that opens on a quote — the split is abandoned and everything is `body`,
|
|
206
|
+
* since losing the sender's actual words is the one outcome worth avoiding.
|
|
101
207
|
*/
|
|
102
208
|
function splitQuotedOriginal(text) {
|
|
103
209
|
const lines = text.split(/\r?\n/);
|
|
@@ -107,10 +213,10 @@ function splitQuotedOriginal(text) {
|
|
|
107
213
|
let separator = '';
|
|
108
214
|
if (/^-{2,}\s*original message\s*-{2,}$/i.test(line))
|
|
109
215
|
separator = '-----Original Message-----';
|
|
110
|
-
// Outlook's HTML thread rule,
|
|
216
|
+
// Outlook's HTML thread rule, directly above the From:/Sent: block.
|
|
111
217
|
else if (/^_{10,}$/.test(line))
|
|
112
218
|
separator = 'Outlook thread rule';
|
|
113
|
-
// "On Tue, Jul 21, 2026 at 9:14 AM
|
|
219
|
+
// "On Tue, Jul 21, 2026 at 9:14 AM Jo Doe <jo@example.com> wrote:"
|
|
114
220
|
else if (/^on\b.{5,300}\bwrote:$/i.test(line))
|
|
115
221
|
separator = 'On … wrote:';
|
|
116
222
|
else if (/^>/.test(lines[i]))
|
package/dist/mail.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mail.js","sourceRoot":"","sources":["../src/mail.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"mail.js","sourceRoot":"","sources":["../src/mail.ts"],"names":[],"mappings":"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","sourcesContent":["// Platform-neutral mail helpers: folder strings, reply prefixes, splitting a\n// reply from the thread it quotes, and what deleting mail reports. Pure\n// functions, usable without Outlook, and shared by both platforms so neither can\n// come to read these differently.\nimport { InvalidRequestError } from './errors';\nimport type { DeleteMailOutcome, DeleteMailResult, MailFolderRef } from './types';\n\n/**\n * Outlook's olDefaultFolders ids for the folders this package addresses by\n * role rather than by name. The ids are Outlook's own, and a folder's NAME is\n * localized, so a role is always resolved through its id.\n */\nexport const FolderId = Object.freeze({\n DeletedItems: 3,\n Outbox: 4,\n SentMail: 5,\n Inbox: 6,\n Drafts: 16,\n Junk: 23,\n});\n\n/**\n * The folders addressable by name rather than by walking from the Inbox, with\n * their olDefaultFolders id. A well-known name wins over a user folder of the\n * same name; reach the latter by qualifying it (`Inbox\\Drafts`).\n */\nexport const WELL_KNOWN_FOLDERS: Readonly<Record<string, number>> = Object.freeze({\n 'inbox': FolderId.Inbox,\n 'sent items': FolderId.SentMail,\n 'sent': FolderId.SentMail,\n 'sent mail': FolderId.SentMail,\n 'drafts': FolderId.Drafts,\n 'deleted items': FolderId.DeletedItems,\n 'deleted': FolderId.DeletedItems,\n 'trash': FolderId.DeletedItems,\n 'junk email': FolderId.Junk,\n 'junk': FolderId.Junk,\n 'outbox': FolderId.Outbox,\n});\n\n/** The Inbox root, as a folder reference. */\nconst INBOX_REF: Readonly<MailFolderRef> = Object.freeze({\n rootId: FolderId.Inbox,\n rootLabel: 'Inbox',\n segments: [],\n});\n\n/**\n * Why `deleteOutlookEmails` refuses an item, worded once for both platforms —\n * it reaches the caller verbatim as a per-item `reason`.\n */\nexport const PROTECTED_MAIL_REASON =\n 'received or sent mail (Inbox/Sent Items or a subfolder) - pass allowProtected to override';\n\nconst DELETE_STATUSES: readonly DeleteMailOutcome['status'][] = ['deleted', 'would-delete', 'refused', 'failed'];\n\n/** The status a delete script reported for one item; anything it shouldn't have said is a failure. */\nexport function deleteStatus(reported: string): DeleteMailOutcome['status'] {\n return DELETE_STATUSES.find(status => status === reported) ?? 'failed';\n}\n\n/** A deleteOutlookEmails result: every outcome, and how many ended each way. */\nexport function deleteMailResult(dryRun: boolean, items: DeleteMailOutcome[]): DeleteMailResult {\n const tally = (status: DeleteMailOutcome['status']) => items.filter(item => item.status === status).length;\n return { dryRun, deleted: tally('deleted'), refused: tally('refused'), failed: tally('failed'), items };\n}\n\n/**\n * Roots whose items are outgoing mail, which carries a sent time where incoming\n * mail carries a received one — so a date filter has to know which it's on.\n */\nexport function isOutgoingRoot(rootId: number): boolean {\n return rootId === FolderId.SentMail || rootId === FolderId.Outbox || rootId === FolderId.Drafts;\n}\n\n/**\n * Roots an Inbox walk must never descend into.\n *\n * On an Exchange profile these sit beside the Inbox and a walk from it can't\n * reach them anyway. On an IMAP profile Outlook nests them UNDER the Inbox, so\n * the same walk would hand back mail already sent, drafted or thrown away as if\n * it had just arrived. They are matched by id, never by name: the names are\n * localized.\n */\nexport const NON_INCOMING_ROOTS: readonly number[] = Object.freeze([\n FolderId.DeletedItems,\n FolderId.Outbox,\n FolderId.SentMail,\n FolderId.Drafts,\n FolderId.Junk,\n]);\n\n/**\n * Subject prefixes that mark a reply or a forward.\n *\n * Kept as a pattern SOURCE because it runs in two engines: macOS tests it as a\n * JavaScript RegExp, Windows inside PowerShell's .NET one, and both read every\n * construct used here the same way. Non-ASCII is written as \\uXXXX so the\n * generated PowerShell stays plain ASCII.\n *\n * Not only English: mail threaded in Chinese, Japanese or Korean replies under\n * 回复:/返信:/답장: and forwards under 转发:/転送:/전달:, and the European\n * clients use AW:, WG:, SV:, TR:, RV:, ENC: and friends. The colon may be the\n * full-width one a CJK input method types, and Outlook's reply counter (RE[2]:)\n * can sit before it.\n */\nconst REPLY_PREFIXES: readonly string[] = [\n 're', 'fwd?', 'aw', 'wg', 'sv', 'vs', 'vb', 'tr', 'res', 'rv', 'enc', 'odp',\n '\\\\u56DE\\\\u590D', '\\\\u56DE\\\\u8986', '\\\\u7B54\\\\u590D', '\\\\u7B54\\\\u8986',\n '\\\\u8F6C\\\\u53D1', '\\\\u8F49\\\\u767C', '\\\\u8FD4\\\\u4FE1', '\\\\u8EE2\\\\u9001',\n '\\\\uB2F5\\\\uC7A5', '\\\\uC804\\\\uB2EC',\n];\n\n/** The reply/forward prefixes as one case-insensitive pattern source. */\nexport const REPLY_PREFIX_SOURCE =\n '^\\\\s*(?:' + REPLY_PREFIXES.join('|') + ')\\\\s*(?:\\\\[\\\\d+\\\\])?\\\\s*[:\\\\uFF1A]';\nexport const REPLY_PREFIX = new RegExp(REPLY_PREFIX_SOURCE, 'i');\n\n/**\n * Whether a subject opens on a reply or forward prefix.\n *\n * This reads what the subject claims, not how the email was made: a forward\n * carries a prefix too, and a client can leave one off a real reply.\n */\nexport function hasReplyPrefix(subject: string): boolean {\n return REPLY_PREFIX.test(subject);\n}\n\n/**\n * The subject every message in a thread shares: every reply and forward prefix\n * stripped, however many clients stacked them (`RE: FW: RE[2]: Rate request`\n * is `Rate request`), and whitespace collapsed. Case is kept, so lower-case\n * both sides to compare two threads.\n */\nexport function threadSubject(subject: string): string {\n let rest = subject;\n // Each pass removes at least the prefix's colon, so this always ends.\n while (REPLY_PREFIX.test(rest)) rest = rest.replace(REPLY_PREFIX, '');\n return rest.replace(/\\s+/g, ' ').trim();\n}\n\n/**\n * A subject glob as an anchored regex source: `*` matches any run, `?` one\n * character, everything else itself. Written with constructs JavaScript and\n * .NET read identically, so both platforms apply exactly the same test.\n */\nexport function subjectGlobSource(glob: string): string {\n let source = '';\n for (const ch of glob) {\n if (ch === '*') source += '.*';\n else if (ch === '?') source += '.';\n else source += ch.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n }\n return `^${source}$`;\n}\n\n/** The leaf name of a folder path or bare name. */\nexport function folderLeafName(entry: string): string {\n return entry.trim().split(/[\\\\/]/).filter(Boolean).pop() ?? '';\n}\n\n/** A full Outlook folder path starts with the mailbox: `\\\\mailbox@example.com\\…`. */\nconst MAILBOX_PREFIX = /^\\\\\\\\[^\\\\/]+[\\\\/]/;\n\nfunction folderSegments(folder: string): string[] {\n return folder.trim().replace(MAILBOX_PREFIX, '').split(/[\\\\/]+/).map(s => s.trim()).filter(Boolean);\n}\n\n/**\n * Resolve a folder string to a well-known root plus the segments below it.\n *\n * Accepts every shape a person has to hand: a bare name ('Invoices'), a\n * relative path ('Invoices\\Paid'), a well-known folder ('Sent Items'), a path\n * under one ('Deleted Items\\2026'), and the full path listInboxFolders prints\n * ('\\\\team@example.com\\Inbox\\Invoices'). Anything not starting at a well-known\n * folder is taken as relative to the Inbox.\n */\nexport function mailFolderRef(folder: string): MailFolderRef {\n const segments = folderSegments(folder);\n const head = segments[0]?.toLowerCase();\n if (head !== undefined && Object.prototype.hasOwnProperty.call(WELL_KNOWN_FOLDERS, head)) {\n return { rootId: WELL_KNOWN_FOLDERS[head], rootLabel: segments[0], segments: segments.slice(1) };\n }\n return { rootId: FolderId.Inbox, rootLabel: 'Inbox', segments };\n}\n\n/**\n * A caller's folder option as a reference: none given means the Inbox root,\n * and a string that names no folder at all (only separators) is refused rather\n * than silently read as the Inbox root.\n */\nexport function folderOption(folder: string | undefined): { ref: MailFolderRef; label: string } {\n if (folder === undefined || folder.trim() === '') return { ref: INBOX_REF, label: 'Inbox' };\n if (folderSegments(folder).length === 0) {\n throw new InvalidRequestError(`Folder '${folder}' does not name a folder.`);\n }\n return { ref: mailFolderRef(folder), label: folder.trim() };\n}\n\n/**\n * Split a plain-text reply into the sender's own text and the quoted thread\n * below it.\n *\n * This matters more than it looks: the quoted original carries the text of the\n * message being answered, and handing a reader one blob invites a figure from\n * that message to be read back as the sender's own.\n *\n * Splits at the earliest separator Outlook or the common webmail clients emit.\n * If that would leave no new text at all — a bottom-posted reply, or a body\n * that opens on a quote — the split is abandoned and everything is `body`,\n * since losing the sender's actual words is the one outcome worth avoiding.\n */\nexport function splitQuotedOriginal(text: string): { body: string; quoted: string; separator: string } {\n const lines = text.split(/\\r?\\n/);\n const whole = { body: text, quoted: '', separator: '' };\n for (let i = 0; i < lines.length; i++) {\n const line = lines[i].trim();\n let separator = '';\n if (/^-{2,}\\s*original message\\s*-{2,}$/i.test(line)) separator = '-----Original Message-----';\n // Outlook's HTML thread rule, directly above the From:/Sent: block.\n else if (/^_{10,}$/.test(line)) separator = 'Outlook thread rule';\n // \"On Tue, Jul 21, 2026 at 9:14 AM Jo Doe <jo@example.com> wrote:\"\n else if (/^on\\b.{5,300}\\bwrote:$/i.test(line)) separator = 'On … wrote:';\n else if (/^>/.test(lines[i])) separator = '> quoted lines';\n // A bare header block: \"From: …\" followed by another header line.\n else if (/^from:\\s*\\S/i.test(line) && /^(sent|date|to|subject|cc):/i.test((lines[i + 1] || '').trim())) {\n separator = 'From:/Sent: header block';\n }\n if (!separator) continue;\n const body = lines.slice(0, i).join('\\n').trimEnd();\n if (!body.trim()) return whole;\n return { body, quoted: lines.slice(i).join('\\n').trim(), separator };\n }\n return whole;\n}\n"]}
|
package/dist/runtime.d.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
+
import { type ScriptRunner } from './errors';
|
|
1
2
|
/** One completed script run, handed to the `debug` hook. */
|
|
2
3
|
export interface BridgeDebugEvent {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
/** The generated script, verbatim — the thing you actually want when a run fails. */
|
|
4
|
+
runner: ScriptRunner;
|
|
5
|
+
/** The script exactly as it ran. */
|
|
6
6
|
script: string;
|
|
7
7
|
durationMs: number;
|
|
8
8
|
/** Present only when the run failed. */
|
|
@@ -10,67 +10,100 @@ export interface BridgeDebugEvent {
|
|
|
10
10
|
}
|
|
11
11
|
export interface BridgeOptions {
|
|
12
12
|
/**
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
13
|
+
* Ceiling on every script run, in milliseconds, replacing each operation's
|
|
14
|
+
* own budget; 0 disables timeouts. Leave unset (or pass null to go back) to
|
|
15
|
+
* let each operation use a budget sized for its work — a minute for a
|
|
16
|
+
* lookup, up to ten for purging Deleted Items. `editEmailTemplate` waits on
|
|
17
|
+
* a person and is never timed; cancel it with `signal`.
|
|
16
18
|
*/
|
|
17
|
-
timeoutMs?: number;
|
|
19
|
+
timeoutMs?: number | null;
|
|
18
20
|
/**
|
|
19
|
-
* Max bytes a script may
|
|
20
|
-
* that
|
|
21
|
-
*
|
|
21
|
+
* Max bytes a script may print before the run is killed with
|
|
22
|
+
* OUTPUT_TOO_LARGE. Searches that return whole bodies push against this.
|
|
23
|
+
* Default 8 MiB.
|
|
22
24
|
*/
|
|
23
25
|
maxBufferBytes?: number;
|
|
24
|
-
/** Directory for generated scripts and for saved attachments when
|
|
25
|
-
* names no destination.
|
|
26
|
+
/** Directory for generated scripts, and for saved attachments when a call
|
|
27
|
+
* names no destination. Default: the OS temp directory. */
|
|
26
28
|
tempDir?: string;
|
|
27
29
|
/**
|
|
28
|
-
* Cancels in-flight runs: the interpreter is killed and the call rejects
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
* Most useful scoped to the calls you want to cancel rather than set process
|
|
33
|
-
* wide: `bridge.withOptions({ signal }).searchInboxByFilter(...)`.
|
|
30
|
+
* Cancels in-flight runs: the interpreter is killed and the call rejects
|
|
31
|
+
* with ABORTED. Most useful scoped to the calls it should cancel:
|
|
32
|
+
* `bridge.withOptions({ signal }).searchInboxByFilter(...)`.
|
|
34
33
|
*/
|
|
35
34
|
signal?: AbortSignal;
|
|
36
|
-
/** `true` logs every
|
|
37
|
-
* instead.
|
|
38
|
-
* non-'0' value. */
|
|
35
|
+
/** `true` logs every script to stderr; a function receives each run
|
|
36
|
+
* instead. Default: on when OUTLOOK_BRIDGE_DEBUG is set to anything but '' or '0'. */
|
|
39
37
|
debug?: boolean | ((event: BridgeDebugEvent) => void);
|
|
40
38
|
}
|
|
41
|
-
|
|
42
|
-
|
|
39
|
+
/** The settings a call runs with, every default applied. */
|
|
40
|
+
export interface ResolvedConfig {
|
|
41
|
+
/** undefined = each operation's own budget. */
|
|
42
|
+
readonly timeoutMs: number | undefined;
|
|
43
|
+
readonly maxBufferBytes: number;
|
|
44
|
+
readonly tempDir: string;
|
|
45
|
+
readonly signal: AbortSignal | undefined;
|
|
46
|
+
readonly debug: boolean | ((event: BridgeDebugEvent) => void);
|
|
47
|
+
}
|
|
48
|
+
/** Caller options merged over a base; keys left out keep the base's value. */
|
|
43
49
|
export declare function mergeOptions(base: ResolvedConfig, options?: BridgeOptions): ResolvedConfig;
|
|
44
50
|
/**
|
|
45
|
-
*
|
|
46
|
-
* rest alone.
|
|
51
|
+
* Change the process-wide settings. Merges, so naming one option leaves the
|
|
52
|
+
* rest alone.
|
|
47
53
|
*
|
|
48
|
-
* Affects every caller in the process. Prefer
|
|
49
|
-
*
|
|
54
|
+
* Affects every caller in the process that uses the exported functions. Prefer
|
|
55
|
+
* `createOutlookBridge(options)` in a process shared with code you don't own.
|
|
50
56
|
*/
|
|
51
57
|
export declare function configure(options: BridgeOptions): void;
|
|
52
|
-
/** The settings in force for the current call
|
|
53
|
-
export declare function getConfig():
|
|
54
|
-
/** The process-wide
|
|
55
|
-
export declare function getGlobalConfig():
|
|
58
|
+
/** The settings in force for the current call: the enclosing bridge's, else the process-wide ones. */
|
|
59
|
+
export declare function getConfig(): ResolvedConfig;
|
|
60
|
+
/** The process-wide settings, ignoring any bridge scope. */
|
|
61
|
+
export declare function getGlobalConfig(): ResolvedConfig;
|
|
56
62
|
/** Run `fn` with `config` in force for it and everything it awaits. */
|
|
57
63
|
export declare function withConfig<T>(config: ResolvedConfig, fn: () => T): T;
|
|
58
|
-
/**
|
|
64
|
+
/**
|
|
65
|
+
* How long each kind of run may take when no `timeoutMs` is set.
|
|
66
|
+
*
|
|
67
|
+
* Generous on purpose: a budget is a guard against a hung interpreter, not a
|
|
68
|
+
* performance target, and the first call against an Outlook that isn't running
|
|
69
|
+
* yet spends much of its budget starting it.
|
|
70
|
+
*/
|
|
71
|
+
export declare const RUN_BUDGETS: {
|
|
72
|
+
/** One lookup: accounts, signatures, opening or reading a single email. */
|
|
73
|
+
readonly quick: 60000;
|
|
74
|
+
/** Composing, filing, saving attachments, or reading one folder. */
|
|
75
|
+
readonly standard: 120000;
|
|
76
|
+
/** Walking a mailbox tree, or acting on a batch of items. */
|
|
77
|
+
readonly scan: 300000;
|
|
78
|
+
/** Emptying Deleted Items. */
|
|
79
|
+
readonly purge: 600000;
|
|
80
|
+
};
|
|
81
|
+
/** A budget name, or 'interactive' for a run that waits on a person and is never timed. */
|
|
82
|
+
export type RunBudget = keyof typeof RUN_BUDGETS | 'interactive';
|
|
83
|
+
/** The timeout one run gets: the configured ceiling if there is one, else its budget. */
|
|
84
|
+
export declare function timeoutFor(budget: RunBudget): number;
|
|
85
|
+
/** Report one finished run to the configured debug hook. */
|
|
59
86
|
export declare function reportRun(event: BridgeDebugEvent): void;
|
|
60
87
|
/**
|
|
61
|
-
*
|
|
62
|
-
* matters: two concurrent calls
|
|
63
|
-
*
|
|
88
|
+
* A scratch-file path in the configured temp directory. The random part
|
|
89
|
+
* matters: two concurrent calls in the same millisecond would otherwise pick the
|
|
90
|
+
* same name and overwrite each other's script mid-run.
|
|
64
91
|
*/
|
|
65
92
|
export declare function tempFile(kind: string, extension: string): string;
|
|
93
|
+
/** Delete a scratch file, ignoring one that is already gone. */
|
|
94
|
+
export declare function removeTempFile(file: string): void;
|
|
95
|
+
/** A destination for saved attachments, and how to undo creating it. */
|
|
96
|
+
export interface AttachmentDestination {
|
|
97
|
+
dir: string;
|
|
98
|
+
/** Remove the directory if this call created it and left it empty. */
|
|
99
|
+
discardIfUnused(): void;
|
|
100
|
+
}
|
|
66
101
|
/**
|
|
67
|
-
*
|
|
102
|
+
* Where one call saves its attachments: the caller's directory (created if
|
|
103
|
+
* absent), or a fresh private directory of its own.
|
|
68
104
|
*
|
|
69
|
-
*
|
|
70
|
-
*
|
|
71
|
-
*
|
|
72
|
-
* unrelated programs using this package on the same machine.
|
|
105
|
+
* Private by default because attachments keep the names senders gave them, so
|
|
106
|
+
* one shared folder means two emails carrying 'invoice.pdf' overwrite each
|
|
107
|
+
* other — and so do two programs using this package on one machine.
|
|
73
108
|
*/
|
|
74
|
-
export declare function
|
|
75
|
-
/** Resolve a caller-supplied destination, creating it when absent. */
|
|
76
|
-
export declare function resolveDestDir(destDir?: string): string;
|
|
109
|
+
export declare function attachmentDestination(destDir?: string): AttachmentDestination;
|