@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/README.md
CHANGED
|
@@ -1,253 +1,222 @@
|
|
|
1
1
|
# @dawnlit/outlook-bridge
|
|
2
2
|
|
|
3
|
-
Drive a real, locally installed Outlook client from Node — read
|
|
4
|
-
with signatures and template emails, file and delete messages. No Graph API, no app registration, no
|
|
5
|
-
this automates the desktop client itself, the
|
|
3
|
+
Drive a real, locally installed Outlook client from Node — read and search mail, send and reply, manage drafts, save
|
|
4
|
+
attachments, work with signatures and template emails, file and delete messages. No Graph API, no app registration, no
|
|
5
|
+
cloud permissions: this automates the desktop client itself, the way a person would.
|
|
6
6
|
|
|
7
|
-
- **Windows** —
|
|
8
|
-
- **macOS** —
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
- Any other platform — `capabilities()` reports everything false and every call rejects with `UNSUPPORTED_PLATFORM`.
|
|
7
|
+
- **Windows** — PowerShell and Outlook's COM object model.
|
|
8
|
+
- **macOS** — AppleScript. Requires **legacy** Outlook for Mac: New Outlook implements only a slice of the AppleScript
|
|
9
|
+
dictionary (accounts don't enumerate, the inbox reports no messages), so every call fails with `ACCOUNT_NOT_FOUND`
|
|
10
|
+
there rather than quietly doing the wrong thing.
|
|
11
|
+
- **Anywhere else** — `capabilities()` reports nothing supported and every call rejects with `UNSUPPORTED_PLATFORM`.
|
|
13
12
|
|
|
14
13
|
## Install
|
|
15
14
|
|
|
16
|
-
Install straight off GitHub `main`, rather than a versioned release:
|
|
17
|
-
|
|
18
15
|
```bash
|
|
19
|
-
npm install
|
|
16
|
+
npm install @dawnlit/outlook-bridge
|
|
20
17
|
```
|
|
21
18
|
|
|
22
|
-
|
|
23
|
-
source with no separate step. npm resolves the branch to a commit and pins it in `package-lock.json`,
|
|
24
|
-
so a later plain `npm install` won't pick up new commits on its own — re-run the command above
|
|
25
|
-
(naming the package and `#main` explicitly) whenever you want to advance to the current tip.
|
|
26
|
-
|
|
27
|
-
Requires Node 24+ and a real Outlook installation on the machine running it — this is desktop automation, not a hosted
|
|
28
|
-
API client.
|
|
19
|
+
Requires Node 24+ and a real Outlook on the machine running it — this is desktop automation, not a hosted API client.
|
|
29
20
|
|
|
30
21
|
## Usage
|
|
31
22
|
|
|
32
23
|
```ts
|
|
33
|
-
import { getOutlookAccounts, readInboxEmails,
|
|
24
|
+
import { getOutlookAccounts, readInboxEmails, readEmailBody, replyOutlookEmail } from '@dawnlit/outlook-bridge';
|
|
25
|
+
|
|
26
|
+
const [account] = await getOutlookAccounts();
|
|
34
27
|
|
|
35
|
-
const
|
|
28
|
+
const recent = await readInboxEmails(account, { daysBack: 7, limit: 20 });
|
|
36
29
|
|
|
37
|
-
|
|
30
|
+
// A listing row can be passed straight back in as the email to act on.
|
|
31
|
+
const email = recent[0];
|
|
32
|
+
const { body } = await readEmailBody(email);
|
|
38
33
|
|
|
39
|
-
await
|
|
40
|
-
emailAccount:
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
htmlBody: '<p>
|
|
44
|
-
|
|
34
|
+
await replyOutlookEmail({
|
|
35
|
+
emailAccount: account,
|
|
36
|
+
entryId: email.entryId,
|
|
37
|
+
storeId: email.storeId,
|
|
38
|
+
htmlBody: '<p>Thanks — received.</p>',
|
|
39
|
+
openDraftWindow: false, // file the reply in Drafts without opening a window
|
|
45
40
|
});
|
|
46
41
|
```
|
|
47
42
|
|
|
48
|
-
Every
|
|
49
|
-
same
|
|
50
|
-
|
|
51
|
-
## Platform differences
|
|
52
|
-
|
|
53
|
-
Both platforms implement the whole contract, so `capabilities()` is all-true on Windows and on macOS. It still exists,
|
|
54
|
-
and is still worth asking, because it answers for the machine you are actually on — on any other OS every entry is
|
|
55
|
-
false and every call rejects:
|
|
43
|
+
Every operation takes its required arguments positionally and everything optional as one options object, with the
|
|
44
|
+
same defaults and the same validation on both platforms.
|
|
56
45
|
|
|
57
|
-
|
|
58
|
-
import { createOutlookBridge } from '@dawnlit/outlook-bridge';
|
|
46
|
+
### Operations
|
|
59
47
|
|
|
60
|
-
|
|
48
|
+
| Area | Operations |
|
|
49
|
+
|------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
|
50
|
+
| Accounts | `getOutlookAccounts` |
|
|
51
|
+
| Reading | `readInboxEmails` (the Inbox root, or any folder by name or path), `searchInboxByFilter` (walks the whole Inbox tree by subject glob or pattern, date window, attachments, folders), `readEmailBody` (the sender's own text, split from the quoted thread), `readSelectedEmail`, `openOutlookEmail`, `locateEmail` (finds an email again by subject, sender and time once its id has gone stale) |
|
|
52
|
+
| Folders | `listInboxFolders`, `moveOutlookEmails` (creates a missing destination chain on request) |
|
|
53
|
+
| Composing | `sendOutlookEmail` (to/cc/bcc, several attachments, a named signature, send or draft; a draft filed without a window returns its id, so a caller can later send exactly the drafts it made), `replyOutlookEmail` (reply or reply-all, from HTML or a saved template) |
|
|
54
|
+
| Drafts | `listOutlookDrafts`, `sendDrafts` (a chosen subset), `sendAllDrafts`, `deleteOutlookDrafts` |
|
|
55
|
+
| Attachments | `saveEmailAttachment`, `saveEmailAttachments` |
|
|
56
|
+
| Cleanup | `deleteOutlookEmails` (refuses received and sent mail unless allowed; dry-run first), `purgeDeletedItems` |
|
|
57
|
+
| Bounces | `cleanUndeliverableEmails`, `readBounceReport` (which sent messages bounced, and for which recipients), `collectBouncedRecipients`, `readSentRecipientGroups` |
|
|
58
|
+
| Signatures & templates | `listOutlookSignatures`, `readOutlookSignatureHtml`, `readTemplateEmails`, `saveTemplateEmail`, `editEmailTemplate` |
|
|
61
59
|
|
|
62
|
-
|
|
60
|
+
Each is documented where it is declared, in `OutlookBridge` (see `dist/types.d.ts`), with every option and default.
|
|
63
61
|
|
|
64
|
-
|
|
65
|
-
|
|
62
|
+
A few pure helpers need no Outlook: `threadSubject` (a subject with every stacked reply and forward prefix stripped, in
|
|
63
|
+
any of the languages `RE:`, `AW:` or `回复:` come in — the subject a whole thread shares), `hasReplyPrefix`,
|
|
64
|
+
`splitQuotedOriginal` (the sender's new text apart from the quoted thread) and `mailFolderRef`.
|
|
66
65
|
|
|
67
|
-
|
|
68
|
-
compile time rather than quietly defaulting to supported.
|
|
66
|
+
### Identifying an email
|
|
69
67
|
|
|
70
|
-
|
|
68
|
+
Listings return an `entryId` and a `storeId`. Operations on one email accept an `EmailRef`: the entry id alone, or any
|
|
69
|
+
object carrying `{ entryId, storeId? }` — so the row itself can be passed back. Passing the row is also the reliable way
|
|
70
|
+
to keep a `storeId` with its own `entryId`; the store id is what lets an email resolve in a mailbox other than the
|
|
71
|
+
default one.
|
|
71
72
|
|
|
72
|
-
**Ids are not portable.** Windows returns MAPI
|
|
73
|
-
ids (`
|
|
74
|
-
|
|
75
|
-
|
|
73
|
+
**Ids are not portable between platforms.** Windows returns MAPI EntryIDs; macOS returns Outlook for macOS small integer
|
|
74
|
+
message ids, and has no store id (it reports `''` and ignores one passed in). Each platform rejects the other's ids with
|
|
75
|
+
`INVALID_REQUEST`. **Moving an email rewrites its id** on both, so ids saved before a move are stale afterward. Keep the
|
|
76
|
+
subject, sender and received time beside a saved id, and `locateEmail` finds the email again from them wherever it has
|
|
77
|
+
moved under the Inbox, on either platform.
|
|
76
78
|
|
|
77
|
-
|
|
78
|
-
server-side `Items.Restrict`; AppleScript has no counterpart, so the macOS walk indexes each folder with bulk property
|
|
79
|
-
reads and applies the same tests here. Same results, different cost — a `daysBack` of 0 over a large mailbox tree is
|
|
80
|
-
considerably heavier on macOS.
|
|
79
|
+
Timestamps are local time as `yyyy-MM-dd HH:mm`, which sorts correctly as a string.
|
|
81
80
|
|
|
82
|
-
|
|
83
|
-
display-name string Windows reports; and `editEmailTemplate` on macOS files the edited draft when you answer Outlook's
|
|
84
|
-
own "save this message?" prompt on close, where Windows asks for Ctrl+S.
|
|
85
|
-
|
|
86
|
-
## Configuration
|
|
81
|
+
### Templates
|
|
87
82
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
`createOutlookBridge()` gives you those on an instance no other caller in the process can disturb:
|
|
83
|
+
A template email is an ordinary email saved in a mailbox folder (default `Templates`) and edited in Outlook. One template
|
|
84
|
+
can hold several variants of a reply between `[[SECTION]] ... [[/SECTION]]` markers around a shared greeting and
|
|
85
|
+
closing, with `{{PLACEHOLDER}}` tokens:
|
|
92
86
|
|
|
93
87
|
```ts
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
88
|
+
await replyOutlookEmail({
|
|
89
|
+
emailAccount: account,
|
|
90
|
+
entryId: email.entryId,
|
|
91
|
+
storeId: email.storeId,
|
|
92
|
+
templateSubject: 'Standard reply',
|
|
93
|
+
templateSection: 'ACCEPT',
|
|
94
|
+
templatePlaceholders: { NOTE: 'We will confirm by Friday.' },
|
|
95
|
+
signatureName: 'Work', // fills {{SIGNATURE}}
|
|
101
96
|
});
|
|
102
|
-
|
|
103
|
-
await bridge.readInboxEmails(account);
|
|
104
97
|
```
|
|
105
98
|
|
|
106
|
-
|
|
107
|
-
|
|
99
|
+
The template, the section, the placeholders and the signature are all resolved inside the package, so a large
|
|
100
|
+
Word-generated template never has to pass through the caller. Composition refuses rather than guesses: a missing section,
|
|
101
|
+
an unknown placeholder, or a marker left unresolved fails with `INVALID_REQUEST` before anything is created.
|
|
108
102
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
103
|
+
The same toolkit works on any HTML string: `composeTemplateBody`, `findTemplateMarkers`, and for `{Token}`-style fills,
|
|
104
|
+
`replaceToken`, `removeTokenLine` (removes a token together with its table row or paragraph), `findTokens` and
|
|
105
|
+
`findUnfilledTokens`.
|
|
112
106
|
|
|
113
|
-
|
|
114
|
-
// → rejects with an AbortedError (code 'ABORTED'), interpreter killed
|
|
115
|
-
```
|
|
107
|
+
## Configuration
|
|
116
108
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
with code you do not own.
|
|
109
|
+
Every call generates a script and runs it under an interpreter. The settings are how long a run may take, how much it may
|
|
110
|
+
print, how to call it off, and what the script said:
|
|
120
111
|
|
|
121
112
|
```ts
|
|
122
|
-
import {
|
|
113
|
+
import { createOutlookBridge } from '@dawnlit/outlook-bridge';
|
|
123
114
|
|
|
124
|
-
|
|
125
|
-
|
|
115
|
+
const outlook = createOutlookBridge({
|
|
116
|
+
timeoutMs: 120_000, // ceiling on every run (default: a budget sized for each operation)
|
|
117
|
+
maxBufferBytes: 32 * 1024 * 1024, // output cap; searches returning bodies push against it (default 8 MiB)
|
|
118
|
+
tempDir: '/var/tmp/outlook', // generated scripts, and attachments saved without a destination
|
|
119
|
+
debug: event => log(event.script),// every generated script, its duration, and any error
|
|
120
|
+
});
|
|
126
121
|
```
|
|
127
122
|
|
|
128
|
-
`
|
|
123
|
+
- **Timeouts.** Unless `timeoutMs` is set, each run gets a budget sized for its work — a minute for a lookup, two for
|
|
124
|
+
composing or reading a folder, five for a mailbox walk or a batch, ten for purging. `timeoutMs` replaces all of them;
|
|
125
|
+
`0` disables timeouts; `null` goes back to the budgets. `editEmailTemplate` waits on a person and is never timed —
|
|
126
|
+
cancel it with a signal.
|
|
127
|
+
- **Instances.** A bridge's settings apply to its own calls only, even when several bridges run at once.
|
|
128
|
+
`outlook.withOptions({ ... })` derives a bridge with some settings changed — which is how a single call gets its own
|
|
129
|
+
budget or cancellation:
|
|
129
130
|
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
131
|
+
```ts
|
|
132
|
+
const controller = new AbortController();
|
|
133
|
+
cancelButton.onclick = () => controller.abort();
|
|
134
|
+
await outlook.withOptions({ signal: controller.signal }).searchInboxByFilter(account, { daysBack: 365 });
|
|
135
|
+
// → rejects with ABORTED, interpreter killed
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
- **Process-wide.** The exported functions run under process-wide settings that `configure()` changes. That is the
|
|
139
|
+
simpler choice for a program that owns its process; prefer `createOutlookBridge` in anything sharing one.
|
|
140
|
+
- **Debugging.** `OUTLOOK_BRIDGE_DEBUG=1` logs every script to stderr without a code change.
|
|
141
|
+
|
|
142
|
+
Capability discovery answers for the machine you are on: `capabilities()` returns a map of every operation to
|
|
143
|
+
`true`/`false`, and `supports('searchInboxByFilter')` asks about one.
|
|
133
144
|
|
|
134
145
|
## Errors
|
|
135
146
|
|
|
136
|
-
Every deliberate failure is an `OutlookError`
|
|
137
|
-
|
|
147
|
+
Every deliberate failure is an `OutlookError` with a stable `code`. **The codes are the API; the messages are not** —
|
|
148
|
+
branch on `code`, never on message text.
|
|
138
149
|
|
|
139
150
|
```ts
|
|
140
151
|
import { OutlookError } from '@dawnlit/outlook-bridge';
|
|
141
152
|
|
|
142
153
|
try {
|
|
143
|
-
await
|
|
154
|
+
await outlook.readInboxEmails(account, { folder: 'Invoices\\2026' });
|
|
144
155
|
} catch (error) {
|
|
145
|
-
if (!(error instanceof OutlookError)) throw error; // a
|
|
156
|
+
if (!(error instanceof OutlookError)) throw error; // a bug
|
|
146
157
|
switch (error.code) {
|
|
147
|
-
case 'ACCOUNT_NOT_FOUND': return
|
|
148
|
-
case '
|
|
149
|
-
case '
|
|
150
|
-
case 'SCRIPT_FAILED': return
|
|
158
|
+
case 'ACCOUNT_NOT_FOUND': return askForAccount(error.account);
|
|
159
|
+
case 'NOT_FOUND': return report(`${error.kind} missing: ${error.message}`);
|
|
160
|
+
case 'OUTPUT_TOO_LARGE': return retryWithNarrowerWindow();
|
|
161
|
+
case 'SCRIPT_FAILED': return logFailure(error.script, error.line, error.stderr);
|
|
151
162
|
}
|
|
152
163
|
}
|
|
153
164
|
```
|
|
154
165
|
|
|
155
|
-
| code
|
|
156
|
-
|
|
157
|
-
| `UNSUPPORTED_PLATFORM` | `UnsupportedPlatformError` | No Outlook automation exists on this OS.
|
|
158
|
-
| `NOT_IMPLEMENTED`
|
|
159
|
-
| `ACCOUNT_NOT_FOUND`
|
|
160
|
-
| `NOT_FOUND`
|
|
161
|
-
| `INVALID_REQUEST`
|
|
162
|
-
| `SCRIPT_FAILED`
|
|
163
|
-
| `
|
|
164
|
-
| `
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
166
|
+
| code | class | meaning |
|
|
167
|
+
|------------------------|----------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
|
168
|
+
| `UNSUPPORTED_PLATFORM` | `UnsupportedPlatformError` | No Outlook automation exists on this OS. |
|
|
169
|
+
| `NOT_IMPLEMENTED` | `NotImplementedError` | The platform could do this, but this corner isn't written (a folder root macOS has no term for). |
|
|
170
|
+
| `ACCOUNT_NOT_FOUND` | `AccountNotFoundError` | No mailbox in the profile matches the address. Carries `account`. |
|
|
171
|
+
| `NOT_FOUND` | `NotFoundError` | A `folder`, `email`, `attachment`, `template`, `signature` or `file` didn't resolve. Carries `kind`. |
|
|
172
|
+
| `INVALID_REQUEST` | `InvalidRequestError` | The arguments can't produce a call — a wrong type, a missing value, a template edited into an unusable state, or a mailbox that can be read but not sent from. |
|
|
173
|
+
| `SCRIPT_FAILED` | `ScriptError` | The script ran and failed. Carries `script`, `line`, `stderr`, `runner`, `durationMs`. |
|
|
174
|
+
| `OUTPUT_TOO_LARGE` | `OutputTooLargeError` | The script printed more than `maxBufferBytes`. |
|
|
175
|
+
| `TIMEOUT` | `TimeoutError` | The run exceeded its time budget and was killed. Carries `timeoutMs`. |
|
|
176
|
+
| `ABORTED` | `AbortedError` | The caller's `AbortSignal` fired. |
|
|
177
|
+
|
|
178
|
+
Batch operations (moving, deleting, sending drafts, cleaning bounces) don't throw for one bad item: they report each
|
|
179
|
+
failure in `failed` beside the items that worked.
|
|
180
|
+
|
|
181
|
+
## Security
|
|
182
|
+
|
|
183
|
+
Caller text ends up inside generated scripts, so the package treats every argument as untrusted:
|
|
184
|
+
|
|
185
|
+
- Arguments are checked for type at run time — a count must be a finite number, a flag a boolean — before any script
|
|
186
|
+
is generated, whatever TypeScript believed at compile time.
|
|
187
|
+
- On Windows, caller text only ever appears in single-quoted PowerShell literals, with every quote character PowerShell
|
|
188
|
+
accepts (including the typographic `‘ ’ ‚ ‛`) escaped. Text that has to appear in a message is referenced through a
|
|
189
|
+
variable, never interpolated into a double-quoted string where `$(...)` would run. Scripts run from UTF-8 files with a
|
|
190
|
+
byte-order mark, so non-ASCII text arrives intact.
|
|
191
|
+
- Attachment names come from the sender, so a saved file's name is sanitized — no path separators, reserved device
|
|
192
|
+
names or characters the file system rejects — and never overwrites an existing file.
|
|
168
193
|
|
|
169
194
|
## Module format
|
|
170
195
|
|
|
171
|
-
Published as CommonJS
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
There is deliberately no dual ESM/CJS build. This package identifies its errors with `instanceof`, and a dual-format
|
|
175
|
-
package can load two copies of itself into one process — at which point `error instanceof OutlookError` returns
|
|
176
|
-
`false` for errors the package itself threw. One format avoids that failure entirely.
|
|
177
|
-
|
|
178
|
-
## API
|
|
179
|
-
|
|
180
|
-
**Accounts & inbox** — `getOutlookAccounts`, `readInboxEmails`, `readEmailBody`,
|
|
181
|
-
`listInboxFolders`, `moveOutlookEmails`, `openOutlookEmail`.
|
|
182
|
-
|
|
183
|
-
**Search & selection** — `searchInboxByFilter` (walks every folder under the Inbox for one
|
|
184
|
-
account, filtering by subject pattern/date window/reply-exclusion/attachment presence — the
|
|
185
|
-
building block for "find the emails matching X" without knowing which subfolder holds them),
|
|
186
|
-
`readSelectedEmail` (the email currently selected or open in Outlook).
|
|
187
|
-
|
|
188
|
-
**Sending & replying** — `sendOutlookEmail`, `replyOutlookEmail` (answers the sender; pass `replyAll: true` to answer
|
|
189
|
-
every recipient), `sendAllDrafts`.
|
|
190
|
-
|
|
191
|
-
**Drafts** — `listOutlookDrafts`, `deleteOutlookDrafts`, `sendDrafts` (sends a chosen subset by
|
|
192
|
-
EntryID — the review-and-select alternative to `sendAllDrafts`'s account-wide sweep).
|
|
193
|
-
|
|
194
|
-
**Attachments** — `saveEmailAttachment`, `saveEmailAttachmentDetailed`, `saveEmailAttachments`
|
|
195
|
-
(the same, batched into one COM round trip for several attachments off one email). All take an optional `destDir`,
|
|
196
|
-
created if absent. Without one, each call saves into a fresh private directory — attachments keep the name the sender
|
|
197
|
-
gave them, so a shared folder means two emails carrying `invoice.pdf` overwrite each other.
|
|
198
|
-
|
|
199
|
-
**Signatures & templates** — `listOutlookSignatures`, `readOutlookSignatureHtml`,
|
|
200
|
-
`readTemplateEmails`, `saveTemplateEmail`, `editEmailTemplate` (opens the template in a real Outlook compose window,
|
|
201
|
-
waits for the user to close it, and returns the saved HTML — so it has no timeout by default; give it an
|
|
202
|
-
`AbortSignal` if you need a way to give up).
|
|
203
|
-
|
|
204
|
-
**Bounce handling** — `cleanUndeliverableEmails`, `collectBouncedRecipients`,
|
|
205
|
-
`readSentRecipientGroups`.
|
|
206
|
-
|
|
207
|
-
**Mailbox cleanup** — `deleteOutlookEmails`, `purgeDeletedItems`.
|
|
208
|
-
|
|
209
|
-
**Template-section parsing** — a small pure-string toolkit for templates that carry several reply variants between
|
|
210
|
-
`[[SECTION]] ... [[/SECTION]]` markers around one shared greeting/signature, with `{{PLACEHOLDER}}` tokens filled in by
|
|
211
|
-
the caller: `findTemplateMarkers`, `composeTemplateBody`, `findTokens`,
|
|
212
|
-
`findUnfilledTokens`, `replaceToken`, `removeTokenLine`.
|
|
213
|
-
|
|
214
|
-
**Platform-neutral helpers** — usable with no Outlook session at all: `mailFolderRef` (parse any folder string a user
|
|
215
|
-
might type into a well-known root plus segments), `splitQuotedOriginal` (separate a reply's own text from the thread
|
|
216
|
-
quoted below it), `WELL_KNOWN_FOLDERS`.
|
|
217
|
-
|
|
218
|
-
**Instances, capabilities and errors** — `createOutlookBridge` (a bridge with its own settings; see
|
|
219
|
-
[Configuration](#configuration)), `capabilities` / `supports`, and the `OutlookError` family
|
|
220
|
-
(see [Errors](#errors)).
|
|
221
|
-
|
|
222
|
-
Every exported type (`InboxEmail`, `ReplyEmailParams`, `MailFolderRef`, `OutlookBridge`, `CapabilityMap`,
|
|
223
|
-
`OutlookErrorCode`, etc.) is exported alongside its function.
|
|
196
|
+
Published as CommonJS; ESM consumers can use named imports normally. There is deliberately no dual build: the package
|
|
197
|
+
identifies its errors with `instanceof`, and a dual-format package can load two copies of itself into one process.
|
|
224
198
|
|
|
225
199
|
## Development
|
|
226
200
|
|
|
227
201
|
```bash
|
|
228
202
|
npm run build # tsc → dist/
|
|
229
|
-
npm test # build, then node:test
|
|
203
|
+
npm test # build, then node:test
|
|
230
204
|
```
|
|
231
205
|
|
|
232
|
-
The tests
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
nested accessor (`address of (sender of m)` does not coerce, and inside a `try` that means a silently empty sender).
|
|
247
|
-
|
|
248
|
-
What no test can cover is the automation itself: driving a real Outlook client is the whole point of the package, and
|
|
249
|
-
behaviour against a live mailbox — that a `move` really filed the mail, that a reply threaded correctly — is verified
|
|
250
|
-
by running it, not in CI.
|
|
206
|
+
The tests need no Outlook. They cover every operation's validation and defaults against a fake backend, the pure
|
|
207
|
+
helpers, the error taxonomy, and the generated scripts themselves:
|
|
208
|
+
|
|
209
|
+
- **On Windows**, every PowerShell script every operation generates is put through PowerShell's own parser, with
|
|
210
|
+
arguments built to break out of a badly escaped literal. The test fails on a syntax error, and on any caller text
|
|
211
|
+
inside a double-quoted string.
|
|
212
|
+
- **Everywhere**, the AppleScript counterpart: every macOS script is checked for caller text outside its string
|
|
213
|
+
literals and for the two mistakes compiling can't catch — an assignment to a reserved AppleScript word, a record
|
|
214
|
+
field read through a nested accessor — and canned script output is decoded into the result shapes Windows returns.
|
|
215
|
+
- **On a Mac**, the hostile argument is also round-tripped through `osascript`, and with Outlook installed every
|
|
216
|
+
AppleScript is compiled with `osacompile`, which resolves dictionary terms without opening a session.
|
|
217
|
+
|
|
218
|
+
What no test covers is the automation itself: that a move really filed the mail, or a reply threaded correctly, is
|
|
219
|
+
verified against a live mailbox.
|
|
251
220
|
|
|
252
221
|
## License
|
|
253
222
|
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
import type { CleanUndeliverableResult, DeleteDraftsResult, DeleteMailResult, EmailLocator, InboxEmail, InboxFolderInfo, InboxSearchMatch, ListDraftsResult, MailFolderRef, MoveEmailsResult, PurgeDeletedItemsResult, ReplyEmailResult, SavedAttachment, SaveTemplateResult, SelectedEmail, SendDraftsResult, SendEmailResult, SentRecipientGroup, TemplateFolderResult } from './types';
|
|
2
|
+
/** What to do with a composed email. */
|
|
3
|
+
export type Disposition =
|
|
4
|
+
/** Send it now. */
|
|
5
|
+
'send'
|
|
6
|
+
/** Open it in a compose window for review. */
|
|
7
|
+
| 'display'
|
|
8
|
+
/** File it into Drafts without a window. */
|
|
9
|
+
| 'save';
|
|
10
|
+
export interface AccountRequest {
|
|
11
|
+
/** The mailbox's SMTP address, trimmed. */
|
|
12
|
+
readonly account: string;
|
|
13
|
+
}
|
|
14
|
+
export interface SendRequest extends AccountRequest {
|
|
15
|
+
/** One entry per recipient, each an address or a display-name form. */
|
|
16
|
+
readonly to: readonly string[];
|
|
17
|
+
readonly cc: readonly string[];
|
|
18
|
+
readonly bcc: readonly string[];
|
|
19
|
+
readonly subject: string;
|
|
20
|
+
readonly htmlBody: string;
|
|
21
|
+
/** Absolute paths, each verified to exist. */
|
|
22
|
+
readonly attachments: readonly string[];
|
|
23
|
+
readonly disposition: Disposition;
|
|
24
|
+
}
|
|
25
|
+
export interface ReplyRequest extends AccountRequest {
|
|
26
|
+
readonly email: EmailLocator;
|
|
27
|
+
/** The HTML to insert above the quoted original, fully composed. */
|
|
28
|
+
readonly html: string;
|
|
29
|
+
readonly replyAll: boolean;
|
|
30
|
+
readonly disposition: Disposition;
|
|
31
|
+
}
|
|
32
|
+
export interface ReadInboxRequest extends AccountRequest {
|
|
33
|
+
readonly folder: MailFolderRef;
|
|
34
|
+
/** The folder as the caller wrote it, for messages. */
|
|
35
|
+
readonly folderLabel: string;
|
|
36
|
+
readonly daysBack: number;
|
|
37
|
+
readonly limit: number;
|
|
38
|
+
/** 0 means no body is read at all. */
|
|
39
|
+
readonly previewChars: number;
|
|
40
|
+
}
|
|
41
|
+
export interface SearchRequest extends AccountRequest {
|
|
42
|
+
readonly daysBack: number;
|
|
43
|
+
readonly subjectLike?: string;
|
|
44
|
+
readonly subjectPattern?: RegExp;
|
|
45
|
+
readonly excludeReplies: boolean;
|
|
46
|
+
readonly requireAttachment: boolean;
|
|
47
|
+
readonly includeFolders: readonly string[];
|
|
48
|
+
readonly excludeFolders: readonly string[];
|
|
49
|
+
readonly includeBody: boolean;
|
|
50
|
+
}
|
|
51
|
+
/** One email as a backend reads it, before any splitting or capping. */
|
|
52
|
+
export interface RawEmail {
|
|
53
|
+
readonly entryId: string;
|
|
54
|
+
readonly subject: string;
|
|
55
|
+
readonly senderName: string;
|
|
56
|
+
readonly senderEmail: string;
|
|
57
|
+
readonly receivedTime: string;
|
|
58
|
+
/** The full plain-text body. */
|
|
59
|
+
readonly body: string;
|
|
60
|
+
readonly attachmentNames: readonly string[];
|
|
61
|
+
}
|
|
62
|
+
export interface ListFoldersRequest extends AccountRequest {
|
|
63
|
+
readonly maxDepth: number;
|
|
64
|
+
}
|
|
65
|
+
export interface MoveRequest extends AccountRequest {
|
|
66
|
+
/** Non-empty, deduplicated. */
|
|
67
|
+
readonly entryIds: readonly string[];
|
|
68
|
+
readonly folder: MailFolderRef;
|
|
69
|
+
readonly folderLabel: string;
|
|
70
|
+
readonly createIfMissing: boolean;
|
|
71
|
+
}
|
|
72
|
+
export interface ListDraftsRequest extends AccountRequest {
|
|
73
|
+
readonly limit: number;
|
|
74
|
+
readonly previewChars: number;
|
|
75
|
+
}
|
|
76
|
+
export interface EntryIdsRequest extends AccountRequest {
|
|
77
|
+
/** Non-empty, deduplicated. */
|
|
78
|
+
readonly entryIds: readonly string[];
|
|
79
|
+
}
|
|
80
|
+
export interface DeleteEmailsRequest extends EntryIdsRequest {
|
|
81
|
+
readonly allowProtected: boolean;
|
|
82
|
+
readonly dryRun: boolean;
|
|
83
|
+
}
|
|
84
|
+
export interface PurgeRequest extends AccountRequest {
|
|
85
|
+
readonly olderThanDays: number;
|
|
86
|
+
readonly dryRun: boolean;
|
|
87
|
+
}
|
|
88
|
+
export interface SaveAttachmentsRequest {
|
|
89
|
+
readonly email: EmailLocator;
|
|
90
|
+
/** Non-empty; matched exactly first, then ignoring case and whitespace. */
|
|
91
|
+
readonly fileNames: readonly string[];
|
|
92
|
+
/** An existing directory to save into. */
|
|
93
|
+
readonly destDir: string;
|
|
94
|
+
}
|
|
95
|
+
export interface CleanUndeliverableRequest extends AccountRequest {
|
|
96
|
+
readonly daysBack: number;
|
|
97
|
+
readonly dryRun: boolean;
|
|
98
|
+
}
|
|
99
|
+
export interface CollectBouncesRequest extends AccountRequest {
|
|
100
|
+
readonly daysBack: number;
|
|
101
|
+
readonly includeDeletedItems: boolean;
|
|
102
|
+
}
|
|
103
|
+
export interface SentGroupsRequest extends AccountRequest {
|
|
104
|
+
readonly daysBack: number;
|
|
105
|
+
readonly limit: number;
|
|
106
|
+
}
|
|
107
|
+
export interface ReadTemplatesRequest extends AccountRequest {
|
|
108
|
+
readonly folder: string;
|
|
109
|
+
readonly limit: number;
|
|
110
|
+
readonly includeBody: boolean;
|
|
111
|
+
/** Only the template with this subject, case-insensitively. */
|
|
112
|
+
readonly subject?: string;
|
|
113
|
+
}
|
|
114
|
+
export interface SaveTemplateRequest extends AccountRequest {
|
|
115
|
+
readonly folder: string;
|
|
116
|
+
readonly subject: string;
|
|
117
|
+
readonly htmlBody: string;
|
|
118
|
+
}
|
|
119
|
+
export interface EditTemplateRequest {
|
|
120
|
+
readonly label: string;
|
|
121
|
+
readonly html: string;
|
|
122
|
+
}
|
|
123
|
+
/** One platform's Outlook automation. */
|
|
124
|
+
export interface Backend {
|
|
125
|
+
getOutlookAccounts(): Promise<string[]>;
|
|
126
|
+
sendOutlookEmail(request: SendRequest): Promise<SendEmailResult>;
|
|
127
|
+
replyOutlookEmail(request: ReplyRequest): Promise<ReplyEmailResult>;
|
|
128
|
+
readInboxEmails(request: ReadInboxRequest): Promise<InboxEmail[]>;
|
|
129
|
+
searchInboxByFilter(request: SearchRequest): Promise<InboxSearchMatch[]>;
|
|
130
|
+
readSelectedEmail(): Promise<SelectedEmail>;
|
|
131
|
+
readEmailBody(email: EmailLocator): Promise<RawEmail>;
|
|
132
|
+
openOutlookEmail(email: EmailLocator): Promise<void>;
|
|
133
|
+
listInboxFolders(request: ListFoldersRequest): Promise<InboxFolderInfo[]>;
|
|
134
|
+
moveOutlookEmails(request: MoveRequest): Promise<MoveEmailsResult>;
|
|
135
|
+
listOutlookDrafts(request: ListDraftsRequest): Promise<ListDraftsResult>;
|
|
136
|
+
sendDrafts(request: EntryIdsRequest): Promise<SendDraftsResult>;
|
|
137
|
+
sendAllDrafts(request: AccountRequest): Promise<SendDraftsResult>;
|
|
138
|
+
deleteOutlookDrafts(request: EntryIdsRequest): Promise<DeleteDraftsResult>;
|
|
139
|
+
deleteOutlookEmails(request: DeleteEmailsRequest): Promise<DeleteMailResult>;
|
|
140
|
+
purgeDeletedItems(request: PurgeRequest): Promise<PurgeDeletedItemsResult>;
|
|
141
|
+
saveEmailAttachments(request: SaveAttachmentsRequest): Promise<SavedAttachment[]>;
|
|
142
|
+
cleanUndeliverableEmails(request: CleanUndeliverableRequest): Promise<CleanUndeliverableResult>;
|
|
143
|
+
collectBouncedRecipients(request: CollectBouncesRequest): Promise<string[]>;
|
|
144
|
+
readSentRecipientGroups(request: SentGroupsRequest): Promise<SentRecipientGroup[]>;
|
|
145
|
+
listOutlookSignatures(): Promise<string[]>;
|
|
146
|
+
/** '' when there is no signature by that name. */
|
|
147
|
+
readOutlookSignatureHtml(name: string): Promise<string>;
|
|
148
|
+
readTemplateEmails(request: ReadTemplatesRequest): Promise<TemplateFolderResult>;
|
|
149
|
+
saveTemplateEmail(request: SaveTemplateRequest): Promise<SaveTemplateResult>;
|
|
150
|
+
editEmailTemplate(request: EditTemplateRequest): Promise<string>;
|
|
151
|
+
}
|
package/dist/backend.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"backend.js","sourceRoot":"","sources":["../src/backend.ts"],"names":[],"mappings":"","sourcesContent":["// What a platform implements.\n//\n// The public operations (bridge.ts) check every argument, apply every default,\n// and do all the work that is the same on both platforms — composing a reply\n// from a template, splitting a body from its quoted thread, choosing where\n// attachments go. What reaches a backend is a request in its final shape: counts\n// are whole numbers in range, strings are trimmed, folders are parsed, and each\n// field the platform's script needs is present. A backend's job is only to turn\n// that request into Outlook automation and its output into the result.\nimport type {\n CleanUndeliverableResult,\n DeleteDraftsResult,\n DeleteMailResult,\n EmailLocator,\n InboxEmail,\n InboxFolderInfo,\n InboxSearchMatch,\n ListDraftsResult,\n MailFolderRef,\n MoveEmailsResult,\n PurgeDeletedItemsResult,\n ReplyEmailResult,\n SavedAttachment,\n SaveTemplateResult,\n SelectedEmail,\n SendDraftsResult,\n SendEmailResult,\n SentRecipientGroup,\n TemplateFolderResult,\n} from './types';\n\n/** What to do with a composed email. */\nexport type Disposition =\n/** Send it now. */\n | 'send'\n /** Open it in a compose window for review. */\n | 'display'\n /** File it into Drafts without a window. */\n | 'save';\n\nexport interface AccountRequest {\n /** The mailbox's SMTP address, trimmed. */\n readonly account: string;\n}\n\nexport interface SendRequest extends AccountRequest {\n /** One entry per recipient, each an address or a display-name form. */\n readonly to: readonly string[];\n readonly cc: readonly string[];\n readonly bcc: readonly string[];\n readonly subject: string;\n readonly htmlBody: string;\n /** Absolute paths, each verified to exist. */\n readonly attachments: readonly string[];\n readonly disposition: Disposition;\n}\n\nexport interface ReplyRequest extends AccountRequest {\n readonly email: EmailLocator;\n /** The HTML to insert above the quoted original, fully composed. */\n readonly html: string;\n readonly replyAll: boolean;\n readonly disposition: Disposition;\n}\n\nexport interface ReadInboxRequest extends AccountRequest {\n readonly folder: MailFolderRef;\n /** The folder as the caller wrote it, for messages. */\n readonly folderLabel: string;\n readonly daysBack: number;\n readonly limit: number;\n /** 0 means no body is read at all. */\n readonly previewChars: number;\n}\n\nexport interface SearchRequest extends AccountRequest {\n readonly daysBack: number;\n readonly subjectLike?: string;\n readonly subjectPattern?: RegExp;\n readonly excludeReplies: boolean;\n readonly requireAttachment: boolean;\n readonly includeFolders: readonly string[];\n readonly excludeFolders: readonly string[];\n readonly includeBody: boolean;\n}\n\n/** One email as a backend reads it, before any splitting or capping. */\nexport interface RawEmail {\n readonly entryId: string;\n readonly subject: string;\n readonly senderName: string;\n readonly senderEmail: string;\n readonly receivedTime: string;\n /** The full plain-text body. */\n readonly body: string;\n readonly attachmentNames: readonly string[];\n}\n\nexport interface ListFoldersRequest extends AccountRequest {\n readonly maxDepth: number;\n}\n\nexport interface MoveRequest extends AccountRequest {\n /** Non-empty, deduplicated. */\n readonly entryIds: readonly string[];\n readonly folder: MailFolderRef;\n readonly folderLabel: string;\n readonly createIfMissing: boolean;\n}\n\nexport interface ListDraftsRequest extends AccountRequest {\n readonly limit: number;\n readonly previewChars: number;\n}\n\nexport interface EntryIdsRequest extends AccountRequest {\n /** Non-empty, deduplicated. */\n readonly entryIds: readonly string[];\n}\n\nexport interface DeleteEmailsRequest extends EntryIdsRequest {\n readonly allowProtected: boolean;\n readonly dryRun: boolean;\n}\n\nexport interface PurgeRequest extends AccountRequest {\n readonly olderThanDays: number;\n readonly dryRun: boolean;\n}\n\nexport interface SaveAttachmentsRequest {\n readonly email: EmailLocator;\n /** Non-empty; matched exactly first, then ignoring case and whitespace. */\n readonly fileNames: readonly string[];\n /** An existing directory to save into. */\n readonly destDir: string;\n}\n\nexport interface CleanUndeliverableRequest extends AccountRequest {\n readonly daysBack: number;\n readonly dryRun: boolean;\n}\n\nexport interface CollectBouncesRequest extends AccountRequest {\n readonly daysBack: number;\n readonly includeDeletedItems: boolean;\n}\n\nexport interface SentGroupsRequest extends AccountRequest {\n readonly daysBack: number;\n readonly limit: number;\n}\n\nexport interface ReadTemplatesRequest extends AccountRequest {\n readonly folder: string;\n readonly limit: number;\n readonly includeBody: boolean;\n /** Only the template with this subject, case-insensitively. */\n readonly subject?: string;\n}\n\nexport interface SaveTemplateRequest extends AccountRequest {\n readonly folder: string;\n readonly subject: string;\n readonly htmlBody: string;\n}\n\nexport interface EditTemplateRequest {\n readonly label: string;\n readonly html: string;\n}\n\n/** One platform's Outlook automation. */\nexport interface Backend {\n getOutlookAccounts(): Promise<string[]>;\n\n sendOutlookEmail(request: SendRequest): Promise<SendEmailResult>;\n\n replyOutlookEmail(request: ReplyRequest): Promise<ReplyEmailResult>;\n\n readInboxEmails(request: ReadInboxRequest): Promise<InboxEmail[]>;\n\n searchInboxByFilter(request: SearchRequest): Promise<InboxSearchMatch[]>;\n\n readSelectedEmail(): Promise<SelectedEmail>;\n\n readEmailBody(email: EmailLocator): Promise<RawEmail>;\n\n openOutlookEmail(email: EmailLocator): Promise<void>;\n\n listInboxFolders(request: ListFoldersRequest): Promise<InboxFolderInfo[]>;\n\n moveOutlookEmails(request: MoveRequest): Promise<MoveEmailsResult>;\n\n listOutlookDrafts(request: ListDraftsRequest): Promise<ListDraftsResult>;\n\n sendDrafts(request: EntryIdsRequest): Promise<SendDraftsResult>;\n\n sendAllDrafts(request: AccountRequest): Promise<SendDraftsResult>;\n\n deleteOutlookDrafts(request: EntryIdsRequest): Promise<DeleteDraftsResult>;\n\n deleteOutlookEmails(request: DeleteEmailsRequest): Promise<DeleteMailResult>;\n\n purgeDeletedItems(request: PurgeRequest): Promise<PurgeDeletedItemsResult>;\n\n saveEmailAttachments(request: SaveAttachmentsRequest): Promise<SavedAttachment[]>;\n\n cleanUndeliverableEmails(request: CleanUndeliverableRequest): Promise<CleanUndeliverableResult>;\n\n collectBouncedRecipients(request: CollectBouncesRequest): Promise<string[]>;\n\n readSentRecipientGroups(request: SentGroupsRequest): Promise<SentRecipientGroup[]>;\n\n listOutlookSignatures(): Promise<string[]>;\n\n /** '' when there is no signature by that name. */\n readOutlookSignatureHtml(name: string): Promise<string>;\n\n readTemplateEmails(request: ReadTemplatesRequest): Promise<TemplateFolderResult>;\n\n saveTemplateEmail(request: SaveTemplateRequest): Promise<SaveTemplateResult>;\n\n editEmailTemplate(request: EditTemplateRequest): Promise<string>;\n}\n"]}
|