@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.
Files changed (142) hide show
  1. package/README.md +152 -183
  2. package/dist/backend.d.ts +151 -0
  3. package/dist/backend.js +3 -0
  4. package/dist/backend.js.map +1 -0
  5. package/dist/bridge.d.ts +56 -0
  6. package/dist/bridge.js +507 -0
  7. package/dist/bridge.js.map +1 -0
  8. package/dist/errors.d.ts +79 -43
  9. package/dist/errors.js +93 -52
  10. package/dist/errors.js.map +1 -1
  11. package/dist/index.d.ts +10 -10
  12. package/dist/index.js +25 -52
  13. package/dist/index.js.map +1 -1
  14. package/dist/mac/accounts.d.ts +7 -2
  15. package/dist/mac/accounts.js +23 -6
  16. package/dist/mac/accounts.js.map +1 -1
  17. package/dist/mac/attachments.d.ts +5 -25
  18. package/dist/mac/attachments.js +40 -99
  19. package/dist/mac/attachments.js.map +1 -1
  20. package/dist/mac/bounces.d.ts +12 -27
  21. package/dist/mac/bounces.js +96 -117
  22. package/dist/mac/bounces.js.map +1 -1
  23. package/dist/mac/cleanup.d.ts +11 -25
  24. package/dist/mac/cleanup.js +44 -107
  25. package/dist/mac/cleanup.js.map +1 -1
  26. package/dist/mac/drafts.d.ts +15 -34
  27. package/dist/mac/drafts.js +113 -186
  28. package/dist/mac/drafts.js.map +1 -1
  29. package/dist/mac/folders.d.ts +12 -21
  30. package/dist/mac/folders.js +44 -59
  31. package/dist/mac/folders.js.map +1 -1
  32. package/dist/mac/index.d.ts +2 -22
  33. package/dist/mac/index.js +19 -81
  34. package/dist/mac/index.js.map +1 -1
  35. package/dist/mac/profile.d.ts +13 -0
  36. package/dist/mac/profile.js +149 -0
  37. package/dist/mac/profile.js.map +1 -0
  38. package/dist/mac/read.d.ts +21 -37
  39. package/dist/mac/read.js +198 -216
  40. package/dist/mac/read.js.map +1 -1
  41. package/dist/mac/run.d.ts +51 -38
  42. package/dist/mac/run.js +138 -103
  43. package/dist/mac/run.js.map +1 -1
  44. package/dist/mac/scripts.d.ts +90 -91
  45. package/dist/mac/scripts.js +167 -126
  46. package/dist/mac/scripts.js.map +1 -1
  47. package/dist/mac/send.d.ts +10 -20
  48. package/dist/mac/send.js +99 -89
  49. package/dist/mac/send.js.map +1 -1
  50. package/dist/mac/signatures.d.ts +2 -2
  51. package/dist/mac/signatures.js +9 -11
  52. package/dist/mac/signatures.js.map +1 -1
  53. package/dist/mac/templates.d.ts +14 -26
  54. package/dist/mac/templates.js +79 -120
  55. package/dist/mac/templates.js.map +1 -1
  56. package/dist/mail.d.ts +80 -46
  57. package/dist/mail.js +179 -73
  58. package/dist/mail.js.map +1 -1
  59. package/dist/runtime.d.ts +76 -43
  60. package/dist/runtime.js +103 -48
  61. package/dist/runtime.js.map +1 -1
  62. package/dist/shared/args.d.ts +48 -0
  63. package/dist/shared/args.js +216 -0
  64. package/dist/shared/args.js.map +1 -0
  65. package/dist/shared/attachmentMatch.d.ts +26 -20
  66. package/dist/shared/attachmentMatch.js +60 -23
  67. package/dist/shared/attachmentMatch.js.map +1 -1
  68. package/dist/shared/bounceReport.d.ts +11 -0
  69. package/dist/shared/bounceReport.js +45 -0
  70. package/dist/shared/bounceReport.js.map +1 -0
  71. package/dist/shared/bounceRules.d.ts +38 -10
  72. package/dist/shared/bounceRules.js +50 -32
  73. package/dist/shared/bounceRules.js.map +1 -1
  74. package/dist/shared/exec.d.ts +21 -0
  75. package/dist/shared/exec.js +47 -0
  76. package/dist/shared/exec.js.map +1 -0
  77. package/dist/shared/json.d.ts +17 -6
  78. package/dist/shared/json.js +35 -24
  79. package/dist/shared/json.js.map +1 -1
  80. package/dist/shared/locate.d.ts +31 -0
  81. package/dist/shared/locate.js +128 -0
  82. package/dist/shared/locate.js.map +1 -0
  83. package/dist/shared/replyBody.d.ts +31 -36
  84. package/dist/shared/replyBody.js +83 -72
  85. package/dist/shared/replyBody.js.map +1 -1
  86. package/dist/shared/templateLimits.d.ts +14 -0
  87. package/dist/shared/templateLimits.js +21 -0
  88. package/dist/shared/templateLimits.js.map +1 -0
  89. package/dist/templateBody.d.ts +69 -0
  90. package/dist/{outlookTemplateSections.js → templateBody.js} +80 -73
  91. package/dist/templateBody.js.map +1 -0
  92. package/dist/types.d.ts +381 -168
  93. package/dist/types.js +8 -9
  94. package/dist/types.js.map +1 -1
  95. package/dist/windows/accounts.d.ts +7 -12
  96. package/dist/windows/accounts.js +19 -34
  97. package/dist/windows/accounts.js.map +1 -1
  98. package/dist/windows/attachments.d.ts +9 -24
  99. package/dist/windows/attachments.js +94 -119
  100. package/dist/windows/attachments.js.map +1 -1
  101. package/dist/windows/bounces.d.ts +13 -26
  102. package/dist/windows/bounces.js +168 -200
  103. package/dist/windows/bounces.js.map +1 -1
  104. package/dist/windows/cleanup.d.ts +23 -23
  105. package/dist/windows/cleanup.js +99 -113
  106. package/dist/windows/cleanup.js.map +1 -1
  107. package/dist/windows/drafts.d.ts +16 -35
  108. package/dist/windows/drafts.js +156 -253
  109. package/dist/windows/drafts.js.map +1 -1
  110. package/dist/windows/folders.d.ts +8 -13
  111. package/dist/windows/folders.js +44 -52
  112. package/dist/windows/folders.js.map +1 -1
  113. package/dist/windows/index.d.ts +2 -27
  114. package/dist/windows/index.js +11 -66
  115. package/dist/windows/index.js.map +1 -1
  116. package/dist/windows/read.d.ts +24 -60
  117. package/dist/windows/read.js +265 -306
  118. package/dist/windows/read.js.map +1 -1
  119. package/dist/windows/run.d.ts +38 -40
  120. package/dist/windows/run.js +151 -112
  121. package/dist/windows/run.js.map +1 -1
  122. package/dist/windows/scripts.d.ts +65 -55
  123. package/dist/windows/scripts.js +109 -85
  124. package/dist/windows/scripts.js.map +1 -1
  125. package/dist/windows/send.d.ts +9 -9
  126. package/dist/windows/send.js +76 -88
  127. package/dist/windows/send.js.map +1 -1
  128. package/dist/windows/signatures.d.ts +4 -10
  129. package/dist/windows/signatures.js +30 -37
  130. package/dist/windows/signatures.js.map +1 -1
  131. package/dist/windows/templates.d.ts +12 -18
  132. package/dist/windows/templates.js +117 -150
  133. package/dist/windows/templates.js.map +1 -1
  134. package/package.json +5 -4
  135. package/dist/OutlookService.d.ts +0 -43
  136. package/dist/OutlookService.js +0 -137
  137. package/dist/OutlookService.js.map +0 -1
  138. package/dist/outlookTemplateSections.d.ts +0 -62
  139. package/dist/outlookTemplateSections.js.map +0 -1
  140. package/dist/shared/capabilities.d.ts +0 -3
  141. package/dist/shared/capabilities.js +0 -9
  142. package/dist/shared/capabilities.js.map +0 -1
package/dist/types.d.ts CHANGED
@@ -1,123 +1,169 @@
1
+ /**
2
+ * Where an email lives, as a listing reported it.
3
+ *
4
+ * Every listing row (`InboxEmail`, `InboxSearchMatch`, `SelectedEmail`, …)
5
+ * carries both fields, so a row can be passed straight back in wherever an
6
+ * `EmailRef` is accepted — which is also the reliable way to keep the two ids
7
+ * from different rows apart.
8
+ */
9
+ export interface EmailLocator {
10
+ /** Windows: the MAPI EntryID. macOS: Outlook for Mac's integer message id. */
11
+ readonly entryId: string;
12
+ /**
13
+ * Windows: the StoreID the email was listed from, which lets it resolve in
14
+ * any mounted mailbox rather than only the default one. macOS has no
15
+ * equivalent and ignores it.
16
+ */
17
+ readonly storeId?: string;
18
+ }
19
+ /** An email to act on: its entry id alone, or anything carrying a locator. */
20
+ export type EmailRef = string | EmailLocator;
21
+ /**
22
+ * Recipients: one address, a comma- or semicolon-separated list, or an array.
23
+ * Display-name forms such as `"Doe, Jo" <jo@example.com>` are kept whole.
24
+ */
25
+ export type Recipients = string | readonly string[];
1
26
  export interface SendEmailParams {
2
- /** SMTP address of the account to send from. Never falls back to the default
3
- * account — an address that doesn't resolve is an error, not a silent switch. */
27
+ /** SMTP address of the mailbox to send from. Never falls back to the
28
+ * default account — an address that doesn't resolve is an error. */
4
29
  emailAccount: string;
5
- /** One or more addresses, comma- or semicolon-separated. */
6
- to: string;
7
- cc?: string;
30
+ to?: Recipients;
31
+ cc?: Recipients;
32
+ bcc?: Recipients;
8
33
  subject: string;
9
34
  htmlBody: string;
10
- attachmentPath?: string;
11
- sendImmediately: boolean;
35
+ /** Absolute paths of files to attach. Each has to exist. */
36
+ attachments?: readonly string[];
37
+ /**
38
+ * An Outlook signature, by the name listOutlookSignatures reports, added to
39
+ * the body: in its `{{SIGNATURE}}` placeholder when it has one, otherwise
40
+ * at the end. Outlook adds no signature of its own to a body set this way,
41
+ * so this is how a composed email gets one.
42
+ */
43
+ signatureName?: string;
44
+ /** Send now. Default false: the email is staged as a draft instead. */
45
+ sendImmediately?: boolean;
12
46
  /**
13
- * Draft mode only. True pops an Outlook compose window per email; false saves
14
- * straight to Drafts with no window — the only workable option for batches.
15
- * Defaults to true, matching the single-email callers that rely on the window.
47
+ * Drafts only. True (the default) opens an Outlook compose window; false
48
+ * files the draft silently into Drafts — the workable choice for batches.
16
49
  */
17
50
  openDraftWindow?: boolean;
18
51
  }
19
- export interface ReplyEmailParams {
52
+ export interface SendEmailResult {
53
+ /**
54
+ * The draft's entry id when the email was filed silently into Drafts
55
+ * (`openDraftWindow: false`) — the id `listOutlookDrafts` reports for it,
56
+ * so a caller can keep it and later send or delete exactly the drafts it
57
+ * made. '' when the email was sent, or opened in a compose window, which
58
+ * files nothing yet; and on macOS in the rare case Drafts gained another
59
+ * draft with the same subject in the same moment, so this one could not be
60
+ * told apart.
61
+ */
62
+ draftEntryId: string;
63
+ }
64
+ export interface ReplyEmailParams extends EmailLocator {
65
+ /** SMTP address of the mailbox to reply from. */
20
66
  emailAccount: string;
21
- entryId: string;
22
- /** StoreID from the same listing row — disambiguates across mailboxes. */
23
- storeId?: string;
24
67
  /**
25
- * HTML inserted above the quoted original (a full document is reduced to its <body>
26
- * content). Optional when `templateSubject` is given — the body is then resolved from
27
- * the saved template server-side, so the caller never has to carry the (often large,
28
- * Word-generated) template HTML.
68
+ * HTML inserted above the quoted original (a full document is reduced to
69
+ * its <body> content). Optional when `templateSubject` is given.
29
70
  */
30
71
  htmlBody?: string;
31
- /** Reply with a saved template's body, resolved by subject from the mailbox — an
32
- * alternative to htmlBody that keeps a big template out of the caller's payload. */
72
+ /**
73
+ * Reply with a template email saved in the mailbox, found by subject, so
74
+ * the caller never carries a large template's HTML itself.
75
+ */
33
76
  templateSubject?: string;
34
- /** Folder holding the template when templateSubject is used (default 'Templates'). */
77
+ /** Folder holding the template. Default 'Templates'. */
35
78
  templateFolder?: string;
36
79
  /**
37
- * Section of the template to keep when one template holds several reply variants
38
- * between [[SECTION]] markers (see outlookTemplateSections). The other sections and
39
- * all markers are stripped before the reply is built.
80
+ * The [[SECTION]] of the template to keep, when one template holds several
81
+ * reply variants (see composeTemplateBody). Required if it has sections.
40
82
  */
41
83
  templateSection?: string;
42
84
  /** `{{PLACEHOLDER}}` → HTML substituted into the composed body. */
43
- templatePlaceholders?: Record<string, string>;
85
+ templatePlaceholders?: Readonly<Record<string, string>>;
44
86
  /**
45
- * Name of an Outlook signature (as listed by listOutlookSignatures) to substitute
46
- * into the template's `{{SIGNATURE}}` placeholder. Resolved by the package rather
47
- * than by the caller, so the signature HTML — images and all — never crosses the wire.
87
+ * An Outlook signature (by the name listOutlookSignatures reports), put in
88
+ * the body's `{{SIGNATURE}}` placeholder, or below the new text when there
89
+ * is none. Resolved here, so the signature's HTML never crosses the
90
+ * caller's boundary.
48
91
  */
49
92
  signatureName?: string;
50
93
  /**
51
- * Answer every recipient of the original (Reply All) rather than just its
52
- * sender. Off by default: widening a reply is the kind of mistake that can't
53
- * be taken back, so it has to be asked for.
94
+ * Answer every recipient of the original instead of only its sender.
95
+ * Default false: widening a reply cannot be taken back, so it is opt-in.
54
96
  */
55
97
  replyAll?: boolean;
98
+ /** Send now. Default false. */
56
99
  sendImmediately?: boolean;
100
+ /** Drafts only: open a compose window (the default) or file silently. */
57
101
  openDraftWindow?: boolean;
58
102
  }
59
103
  export interface ReplyEmailResult {
104
+ /** Who the reply is addressed to. Windows reports Outlook's To line
105
+ * (display names); macOS the resolved addresses. */
60
106
  to: string;
61
107
  subject: string;
62
- /** The original email's sender — verify it's who you meant to answer. */
108
+ /** The original email's sender — check it is who you meant to answer. */
63
109
  repliedToSender: string;
64
110
  }
65
- /** One draft that couldn't be sent, identified by its subject for the report. */
66
- export interface DraftSendFailure {
111
+ /** One item a batch operation could not handle, and why. */
112
+ export interface ItemFailure {
113
+ entryId: string;
114
+ /** The item's subject when it could be read, else ''. */
67
115
  subject: string;
68
116
  error: string;
69
117
  }
70
- export interface SendAllDraftsResult {
71
- sent: number;
72
- failed: DraftSendFailure[];
73
- }
74
118
  /** One mail draft belonging to an account. */
75
119
  export interface OutlookDraft {
76
120
  entryId: string;
77
121
  subject: string;
78
- /** The To line as Outlook renders it (display names). */
122
+ /** The To line as Outlook renders it. */
79
123
  to: string;
80
- /** Recipient addresses, best-effort — an unresolved Exchange entry falls back to its name. */
124
+ /** Recipient addresses, best effort — an unresolved Exchange entry falls back to its name. */
81
125
  toEmails: string[];
82
- /** Short plain-text preview — enough to tell one template section from another. */
126
+ /** Short plain-text preview, enough to tell similar drafts apart. */
83
127
  bodyPreview: string;
84
128
  hasAttachments: boolean;
85
129
  lastModified: string;
86
- /** Which Drafts folder it sits in, since two can hold one account's mail. */
130
+ /** The Drafts folder it sits in; one account's drafts can span two. */
87
131
  folderPath: string;
88
132
  }
133
+ export interface ListDraftsOptions {
134
+ /** Newest-first cap on the drafts returned. Default 100. */
135
+ limit?: number;
136
+ /** Characters of body preview per draft. Default 300; 0 omits it. */
137
+ previewChars?: number;
138
+ }
89
139
  export interface ListDraftsResult {
90
140
  account: string;
91
141
  /** The Drafts folders scanned, in scan order. */
92
142
  foldersScanned: string[];
93
- /** Total matching drafts found, before `limit` was applied. */
143
+ /** Matching drafts found, before `limit` was applied. */
94
144
  count: number;
95
145
  truncated: boolean;
96
146
  drafts: OutlookDraft[];
97
147
  }
148
+ export interface SendDraftsResult {
149
+ sent: number;
150
+ failed: ItemFailure[];
151
+ }
98
152
  export interface DeleteDraftsResult {
99
153
  deleted: number;
100
- failed: {
101
- entryId: string;
102
- error: string;
103
- }[];
104
- }
105
- /** One draft that couldn't be sent in a selective send, identified by both id and
106
- * subject — id for the caller to retry or reconcile, subject for a human report. */
107
- export interface SendDraftsFailure {
108
- entryId: string;
109
- subject: string;
110
- error: string;
154
+ failed: ItemFailure[];
111
155
  }
112
- export interface SendDraftsResult {
113
- sent: number;
114
- failed: SendDraftsFailure[];
156
+ export interface DeleteMailOptions {
157
+ /** Lift the refusal to touch Inbox and Sent Items (and their subfolders). */
158
+ allowProtected?: boolean;
159
+ /** Resolve and report every id without deleting anything. */
160
+ dryRun?: boolean;
115
161
  }
116
162
  /** What happened to one id in a deleteOutlookEmails call. */
117
163
  export interface DeleteMailOutcome {
118
164
  entryId: string;
119
165
  subject: string;
120
- /** The folder the item was actually in — the audit trail for what you deleted. */
166
+ /** The folder the item was actually in — the audit trail for a wrong id. */
121
167
  folderPath: string;
122
168
  status: 'deleted' | 'would-delete' | 'refused' | 'failed';
123
169
  reason: string;
@@ -129,10 +175,10 @@ export interface DeleteMailResult {
129
175
  failed: number;
130
176
  items: DeleteMailOutcome[];
131
177
  }
132
- export interface DeleteMailOptions {
133
- /** Lift the Inbox/Sent Items refusal, taking responsibility for reaching received mail. */
134
- allowProtected?: boolean;
135
- /** Resolve and report without deleting anything. */
178
+ export interface PurgeDeletedItemsOptions {
179
+ /** Keep items newer than this many days. Default 0: purge everything. */
180
+ olderThanDays?: number;
181
+ /** Count what would be purged without destroying anything. */
136
182
  dryRun?: boolean;
137
183
  }
138
184
  export interface PurgeDeletedItemsResult {
@@ -145,86 +191,195 @@ export interface PurgeDeletedItemsResult {
145
191
  kept: number;
146
192
  failed: number;
147
193
  }
148
- /** One bounce-back / non-delivery message found in the inbox. */
194
+ /** One bounce-back / non-delivery message found in a mailbox. */
149
195
  export interface UndeliverableEmail {
150
196
  entryId: string;
151
197
  subject: string;
152
198
  senderName: string;
153
199
  senderEmail: string;
154
200
  receivedTime: string;
155
- /** Which rule flagged it (NDR class, mail-daemon sender, or a bounce subject phrase). */
201
+ /** Which rule flagged it: an NDR message class, a mail-daemon sender, or a bounce subject phrase. */
156
202
  matchedReason: string;
157
- /** Best-effort recipient addresses parsed from the bounce body — which sends failed. */
203
+ /** Best-effort addresses parsed from the bounce body — the sends that failed. */
158
204
  failedRecipients: string[];
159
205
  }
206
+ export interface CleanUndeliverableOptions {
207
+ /** How far back to scan. Default 30. */
208
+ daysBack?: number;
209
+ /** Report without deleting. Default TRUE — deleting has to be asked for. */
210
+ dryRun?: boolean;
211
+ }
160
212
  export interface CleanUndeliverableResult {
161
213
  account: string;
162
214
  scannedDays: number;
163
- /** True when nothing was deleted (preview run). */
164
215
  dryRun: boolean;
165
216
  matchedCount: number;
166
217
  deletedCount: number;
167
218
  matched: UndeliverableEmail[];
168
- /** Matched items that could not be deleted (only populated on a delete run). */
169
- failed: DraftSendFailure[];
219
+ /** Matched items that could not be deleted (a delete run only). */
220
+ failed: ItemFailure[];
221
+ }
222
+ export interface CollectBouncedRecipientsOptions {
223
+ /** How far back to scan. Default 30. */
224
+ daysBack?: number;
225
+ /** Scan Deleted Items too, so bounces already cleaned away still count. Default true. */
226
+ includeDeletedItems?: boolean;
170
227
  }
171
- /** One sent message and the full set of SMTP addresses it went to. */
228
+ export interface SentRecipientGroupsOptions {
229
+ /** How far back to read Sent Items. Default 30. */
230
+ daysBack?: number;
231
+ /** Newest-first cap on messages. Default 3000. */
232
+ limit?: number;
233
+ }
234
+ /** One sent message and every SMTP address it went to (To, CC and BCC). */
172
235
  export interface SentRecipientGroup {
173
236
  entryId: string;
174
237
  subject: string;
175
238
  sentOn: string;
176
239
  recipients: string[];
177
240
  }
178
- export interface InboxEmail {
241
+ export interface BounceReportOptions {
242
+ /** How far back to read bounce-backs and Sent Items. Default 30. */
243
+ daysBack?: number;
244
+ /** Count bounces already moved to Deleted Items too. Default true. */
245
+ includeDeletedItems?: boolean;
246
+ }
247
+ /** A sent message that at least one of its recipients bounced back from. */
248
+ export interface BouncedSend {
249
+ entryId: string;
250
+ subject: string;
251
+ sentOn: string;
252
+ /** Every address it went to, lower-cased and deduplicated. */
253
+ recipients: string[];
254
+ /** The recipients a bounce-back reported as failed. */
255
+ failedRecipients: string[];
256
+ /** True when every recipient failed: the message reached nobody. */
257
+ allFailed: boolean;
258
+ }
259
+ export interface BounceReport {
260
+ account: string;
261
+ scannedDays: number;
262
+ /** Every distinct address the bounce-backs reported, lower-cased. */
263
+ bouncedAddresses: string[];
264
+ /** The sent messages with a bounced recipient, newest first. */
265
+ sends: BouncedSend[];
266
+ /**
267
+ * Bounced addresses no sent message in the window went to: an alias the
268
+ * server expanded, or a send older than the window.
269
+ */
270
+ unmatchedAddresses: string[];
271
+ }
272
+ export interface ReadInboxOptions {
273
+ /**
274
+ * Read this folder instead of the Inbox root: a folder under the Inbox by
275
+ * name or path, a well-known folder ('Sent Items', 'Drafts', …), or a full
276
+ * folder path — see mailFolderRef. Only that folder is read, not its
277
+ * subfolders.
278
+ */
279
+ folder?: string;
280
+ /** Only mail from the last this-many days. Default 60. */
281
+ daysBack?: number;
282
+ /** Newest-first cap on rows. Default 50. */
283
+ limit?: number;
284
+ /** Characters of plain-text body per row. Default 600; 0 skips reading bodies. */
285
+ previewChars?: number;
286
+ }
287
+ export interface InboxEmail extends EmailLocator {
179
288
  entryId: string;
180
289
  storeId: string;
181
290
  subject: string;
291
+ /** For outgoing folders (Sent Items, Outbox, Drafts) these carry the
292
+ * RECIPIENT, since a folder of mail "from" its own owner says nothing. */
182
293
  senderName: string;
183
294
  senderEmail: string;
184
295
  receivedTime: string;
185
296
  bodyPreview: string;
186
297
  attachmentNames: string[];
187
298
  attachmentCount: number;
188
- /** Outlook path of the folder each email was read from, so a caller that
189
- * scoped to a subfolder can confirm which folder actually matched.
190
- * Populated on both platforms. */
191
- folderPath?: string;
299
+ /** The folder the email was read from. */
300
+ folderPath: string;
192
301
  }
193
302
  export interface InboxSearchFilter {
194
- /** Glob-style subject pattern — `*` and `?` wildcards, e.g. '*invoice*'.
195
- * Handed to Items.Restrict as the server-side prefilter that keeps a
196
- * full-mailbox walk cheap, and re-checked against every subject that
197
- * comes back, so it filters the result set on stores that won't run the
198
- * query as well. Omit to restrict on date only. */
303
+ /** Only mail from the last this-many days. Default 60 — a scan has no
304
+ * unbounded mode; ask for 365 to reach back a year. */
305
+ daysBack?: number;
306
+ /** Glob-style subject pattern: `*` matches any run, `?` one character,
307
+ * case-insensitive, e.g. '*invoice*'. */
199
308
  subjectLike?: string;
200
- /** Regex re-checked client-side against each survivor's trimmed subject,
201
- * since Restrict's `like` is a blunt substring match. A normal JS
202
- * RegExp — only its `.source` crosses into the PowerShell/.NET regex
203
- * engine, which reads the same syntax; `-match` is case-insensitive
204
- * there by default regardless of the JS pattern's `i` flag. */
309
+ /**
310
+ * Regular expression tested against each subject, case-insensitively
311
+ * whatever the RegExp's own flags; on macOS a `u` or `v` flag is kept,
312
+ * since it decides how the pattern parses. On Windows the pattern runs in
313
+ * .NET's regex engine, so keep to syntax JavaScript and .NET share.
314
+ */
205
315
  subjectPattern?: RegExp;
206
- /** Drop subjects starting with Re:/Fw:/Fwd:. */
316
+ /** Drop subjects carrying a reply or forward prefix (RE:, FW:, AW:, 回复:, …). */
207
317
  excludeReplies?: boolean;
208
- /** Only return items carrying at least one attachment. */
318
+ /** Only return mail with at least one attachment. */
209
319
  requireAttachment?: boolean;
320
+ /**
321
+ * Folders under the Inbox to leave out, by full folder path or bare name,
322
+ * case-insensitively. An excluded folder takes its whole subtree with it.
323
+ * Sent, Drafts, Deleted and Junk are always skipped, even where the profile
324
+ * nests them under the Inbox. macOS matches the leaf name only.
325
+ */
326
+ excludeFolders?: readonly string[];
327
+ /**
328
+ * Scan only these folders, given the same way. Matched exactly: naming a
329
+ * folder does not bring its children, and the Inbox root is included only
330
+ * when named. Exclusion wins where the two overlap.
331
+ */
332
+ includeFolders?: readonly string[];
333
+ /** Return each match's full plain-text body. Default true; false makes a
334
+ * scan much lighter when only the metadata is needed. */
335
+ includeBody?: boolean;
210
336
  }
211
- export interface InboxSearchMatch {
337
+ export interface InboxSearchMatch extends EmailLocator {
212
338
  entryId: string;
213
- /** Store the item lives in — pass alongside entryId so it resolves unambiguously
214
- * across mailboxes (replyOutlookEmail, saveEmailAttachments). */
215
339
  storeId: string;
216
340
  subject: string;
217
341
  senderName: string;
218
- /** SMTP address; resolved from the Exchange DN when the sender is an EX recipient. */
342
+ /** SMTP address, resolved from the Exchange DN where the sender is an Exchange user. */
219
343
  senderEmail: string;
220
- /** 'yyyy-MM-dd HH:mm'. */
221
344
  receivedTime: string;
345
+ /** The plain-text body; '' when the search asked for no bodies. */
222
346
  body: string;
223
- /** Not saved to disk — pass the ones you want through saveEmailAttachments. */
347
+ /** Not saved to disk — pass the ones you want to saveEmailAttachments. */
224
348
  attachmentNames: string[];
225
349
  folderPath: string;
226
350
  }
227
- export interface SelectedEmail {
351
+ /**
352
+ * What was kept about an email besides its id, for finding it again once the
353
+ * id no longer resolves: Outlook rewrites an email's id whenever it moves, and
354
+ * an id from one platform means nothing on the other.
355
+ *
356
+ * At least two of `subject`, `sender` and `receivedTime` are needed, since a
357
+ * subject alone names every message in a thread.
358
+ */
359
+ export interface EmailDescription {
360
+ /** Compared without reply and forward prefixes, spacing or case. */
361
+ subject?: string;
362
+ /**
363
+ * An address, a display name, or both (`Jo Doe <jo@example.com>`). An
364
+ * address is compared as an address; a bare name only against the
365
+ * sender's display name.
366
+ */
367
+ sender?: string;
368
+ /** `yyyy-MM-dd HH:mm` as a listing reported it, or just the day. */
369
+ receivedTime?: string;
370
+ /** The mailbox to search. Default: every account, the one `folderPath` names first. */
371
+ emailAccount?: string;
372
+ /**
373
+ * The folderPath a listing reported (`\\team@example.com\Inbox\Invoices`).
374
+ * Only decides which mailbox is searched first.
375
+ */
376
+ folderPath?: string;
377
+ }
378
+ export interface LocateEmailOptions {
379
+ /** How far back to search when the description has no receivedTime. Default 60. */
380
+ daysBack?: number;
381
+ }
382
+ export interface SelectedEmail extends EmailLocator {
228
383
  entryId: string;
229
384
  storeId: string;
230
385
  subject: string;
@@ -232,8 +387,23 @@ export interface SelectedEmail {
232
387
  senderEmail: string;
233
388
  receivedTime: string;
234
389
  body: string;
235
- /** Not saved to disk — pass the ones you want through saveEmailAttachments. */
390
+ /** Not saved to disk — pass the ones you want to saveEmailAttachments. */
236
391
  attachmentNames: string[];
392
+ /**
393
+ * The folder it sits in, in the form the listings report
394
+ * (`\\team@example.com\[Gmail]\Drafts`) and so naming its mailbox. '' when
395
+ * Outlook can't say: an email opened from a file, or on macOS one whose
396
+ * mailbox neither AppleScript nor the profile database names.
397
+ */
398
+ folderPath: string;
399
+ }
400
+ export interface ReadEmailBodyOptions {
401
+ /** Cap on the returned body. Default 8000. */
402
+ maxChars?: number;
403
+ /** Also return the quoted thread below the reply. Default false. */
404
+ includeQuoted?: boolean;
405
+ /** Cap on the quoted thread when it is returned. Default 4000. */
406
+ maxQuotedChars?: number;
237
407
  }
238
408
  export interface EmailBodyResult {
239
409
  entryId: string;
@@ -243,144 +413,187 @@ export interface EmailBodyResult {
243
413
  receivedTime: string;
244
414
  /** The sender's own new text — everything above the quoted thread. */
245
415
  body: string;
246
- /** True when `body` hit maxChars and was cut. */
416
+ /** True when `body` was cut at maxChars. */
247
417
  truncated: boolean;
248
- /** Length of the sender's new text before any capping. */
418
+ /** Length of the sender's new text before any cut. */
249
419
  bodyLength: number;
250
- /** What the quoted thread was split on ("-----Original Message-----", …); empty when none was found. */
420
+ /** What the quoted thread was split on; '' when none was found. */
251
421
  quoteSeparator: string;
252
422
  /** Length of the quoted thread, reported even when it isn't returned. */
253
423
  quotedLength: number;
254
- /** The quoted thread — only populated when the caller asked for it. */
424
+ /** The quoted thread — only when asked for. */
255
425
  quotedOriginal: string;
256
426
  attachmentNames: string[];
257
427
  attachmentCount: number;
258
428
  }
259
429
  export interface MailFolderRef {
260
- /** olDefaultFolders id of the root the walk starts from. */
430
+ /** olDefaultFolders id of the well-known root the path starts from. */
261
431
  rootId: number;
262
- /** That root's name, for error messages. */
432
+ /** That root as the caller spelled it, for messages. */
263
433
  rootLabel: string;
264
434
  /** Path segments below the root. */
265
435
  segments: string[];
266
436
  }
437
+ export interface ListFoldersOptions {
438
+ /** How many levels below the Inbox to list, 1–10. Default 2. */
439
+ maxDepth?: number;
440
+ }
267
441
  export interface InboxFolderInfo {
268
442
  name: string;
269
- /** Outlook folder path, e.g. "\\\\mailbox\\Inbox\\Invoices". */
443
+ /** Outlook folder path, e.g. `\\mailbox\Inbox\Invoices`. */
270
444
  folderPath: string;
271
445
  itemCount: number;
272
- /** 1 = direct child of Inbox, 2 = grandchild, … */
446
+ /** 1 = direct child of the Inbox, 2 = grandchild, … */
273
447
  depth: number;
274
448
  }
449
+ export interface MoveEmailsOptions {
450
+ /** Create the destination — the whole missing chain — when absent. Default false. */
451
+ createIfMissing?: boolean;
452
+ }
275
453
  export interface MoveEmailsResult {
276
454
  folderPath: string;
277
455
  folderCreated: boolean;
278
456
  moved: number;
279
- failed: {
280
- entryId: string;
281
- error: string;
282
- }[];
457
+ failed: ItemFailure[];
458
+ }
459
+ export interface SaveAttachmentOptions {
460
+ /**
461
+ * Directory to save into, created if absent. Without one, each call saves
462
+ * into a fresh private directory, so two emails that both carry
463
+ * 'invoice.pdf' can never overwrite each other.
464
+ */
465
+ destDir?: string;
283
466
  }
284
467
  export interface SavedAttachment {
468
+ /** Where the file was written. A name already taken in the directory gets
469
+ * a numbered suffix rather than overwriting what is there. */
285
470
  path: string;
471
+ /** The attachment's name as the email carries it. */
472
+ fileName: string;
473
+ /** Who the email was from — confirm it is the email you meant. */
286
474
  subject: string;
287
475
  senderName: string;
288
476
  senderEmail: string;
289
477
  receivedTime: string;
290
478
  }
479
+ export interface ReadTemplatesOptions {
480
+ /** Mailbox folder holding the templates, searched up to 3 levels deep. Default 'Templates'. */
481
+ folder?: string;
482
+ /** Return only the template with this subject (case-insensitive). */
483
+ subject?: string;
484
+ /** Return each full HTML body. Default true; false returns previews. */
485
+ includeBody?: boolean;
486
+ /** Newest-first cap on templates, 1–200. Default 20. */
487
+ limit?: number;
488
+ }
291
489
  /** One template email stored in a mailbox folder. */
292
490
  export interface TemplateEmail {
293
491
  entryId: string;
294
492
  subject: string;
295
- /** Full HTML body — reusable verbatim as another email's htmlBody. Empty when the
296
- * listing was requested without bodies (includeBody:false). */
493
+ /** Full HTML body with embedded (cid:) images removed; '' without includeBody. */
297
494
  htmlBody: string;
298
- /** Short plain-text preview, returned instead of htmlBody when includeBody is false. */
299
- bodyPreview?: string;
300
- /** [[SECTION]] names when the template holds several reply variants (see
301
- * outlookTemplateSections) — returned with or without the body, so a caller can
302
- * confirm the sections it's about to ask for still exist. */
303
- sections?: string[];
304
- /** `{{PLACEHOLDER}}` names the template expects to have filled in. */
305
- placeholders?: string[];
495
+ /** Short plain-text preview; '' with includeBody. */
496
+ bodyPreview: string;
497
+ /** [[SECTION]] names, returned with or without the body. */
498
+ sections: string[];
499
+ /** `{{PLACEHOLDER}}` names the template expects filled. */
500
+ placeholders: string[];
306
501
  lastModified: string;
307
502
  }
308
503
  export interface TemplateFolderResult {
309
504
  folderFound: boolean;
310
- /** Outlook folder path when found, e.g. "\\\\mailbox\\Templates". */
505
+ /** The folder's path when found. */
311
506
  folderPath: string;
312
507
  templates: TemplateEmail[];
313
- /** When the folder wasn't found: the mailbox's folder names, so the caller can pick or create one. */
508
+ /** When the folder wasn't found: the mailbox's top-level folder names. */
314
509
  availableFolders: string[];
315
510
  }
511
+ export interface SaveTemplateParams {
512
+ subject: string;
513
+ htmlBody: string;
514
+ /** Folder to save into, created at the mailbox root if absent. Default 'Templates'. */
515
+ folder?: string;
516
+ }
316
517
  export interface SaveTemplateResult {
317
518
  folderPath: string;
318
519
  folderCreated: boolean;
319
520
  }
320
521
  /**
321
- * What every platform implementation provides. `OutlookService` picks one at load
322
- * time and exposes it under these exact signatures.
323
- *
324
- * Not everything is implemented everywhere — macOS in particular still throws for
325
- * a good part of this. That is a runtime answer ("not implemented on this
326
- * platform"), deliberately not a type-level one: a consumer writing against the
327
- * package should see one signature per function, not a per-platform subset they
328
- * have to narrow.
522
+ * Everything the package does, under one signature per operation whatever the
523
+ * platform. A platform that cannot do something says so at run time — see
524
+ * `CapabilityMap` — rather than through a narrower type.
329
525
  */
330
526
  export interface OutlookBridge {
527
+ /** Every mailbox this Outlook profile can reach, as SMTP addresses. */
331
528
  getOutlookAccounts(): Promise<string[]>;
332
- sendOutlookEmail(params: SendEmailParams): Promise<void>;
529
+ /** Send an email, or stage it as a draft. */
530
+ sendOutlookEmail(params: SendEmailParams): Promise<SendEmailResult>;
531
+ /** Reply to an email, with the new text above the quoted original. */
333
532
  replyOutlookEmail(params: ReplyEmailParams): Promise<ReplyEmailResult>;
334
- sendAllDrafts(emailAccount: string): Promise<SendAllDraftsResult>;
335
- readInboxEmails(emailAccount: string, daysBack?: number, limit?: number, folder?: string): Promise<InboxEmail[]>;
336
- searchInboxByFilter(emailAccount: string, filter?: InboxSearchFilter, daysBack?: number): Promise<InboxSearchMatch[]>;
533
+ /** Recent mail from the Inbox root or one named folder, newest first. */
534
+ readInboxEmails(emailAccount: string, options?: ReadInboxOptions): Promise<InboxEmail[]>;
535
+ /** Walk every folder under the Inbox for the mail matching `filter`. */
536
+ searchInboxByFilter(emailAccount: string, filter?: InboxSearchFilter): Promise<InboxSearchMatch[]>;
537
+ /** The email currently selected, or open, in Outlook. */
337
538
  readSelectedEmail(): Promise<SelectedEmail>;
338
- readEmailBody(entryId: string, storeId?: string, maxChars?: number, includeQuoted?: boolean): Promise<EmailBodyResult>;
339
- openOutlookEmail(entryId: string, storeId?: string): Promise<void>;
340
- listInboxFolders(emailAccount: string, maxDepth?: number): Promise<InboxFolderInfo[]>;
341
- moveOutlookEmails(emailAccount: string, entryIds: string[], folderName: string, createIfMissing?: boolean): Promise<MoveEmailsResult>;
342
- listOutlookDrafts(emailAccount: string, limit?: number, previewChars?: number): Promise<ListDraftsResult>;
343
- deleteOutlookDrafts(emailAccount: string, entryIds: string[]): Promise<DeleteDraftsResult>;
539
+ /** One email's plain-text body, split from the thread it quotes. */
540
+ readEmailBody(email: EmailRef, options?: ReadEmailBodyOptions): Promise<EmailBodyResult>;
541
+ /** Open an email in Outlook. */
542
+ openOutlookEmail(email: EmailRef): Promise<void>;
344
543
  /**
345
- * Send a chosen subset of an account's drafts by EntryID — e.g. after
346
- * `listOutlookDrafts` and a user review pass — rather than the account-wide
347
- * `sendAllDrafts`. Same "must be THIS account's draft" gate as
348
- * `deleteOutlookDrafts`: an id pointing at ordinary mail, or at another
349
- * account's draft, is refused and reported rather than sent.
544
+ * Find an email again from what was kept about it, when its id no longer
545
+ * resolves. Searches the Inbox and its subfolders, and returns null rather
546
+ * than a guess: every field the description gives has to agree.
350
547
  */
351
- sendDrafts(emailAccount: string, entryIds: string[]): Promise<SendDraftsResult>;
352
- deleteOutlookEmails(emailAccount: string, entryIds: string[], options?: DeleteMailOptions): Promise<DeleteMailResult>;
353
- purgeDeletedItems(emailAccount: string, olderThanDays?: number, dryRun?: boolean): Promise<PurgeDeletedItemsResult>;
354
- saveEmailAttachment(entryId: string, fileName: string, storeId?: string, destDir?: string): Promise<string>;
355
- saveEmailAttachmentDetailed(entryId: string, fileName: string, storeId?: string, destDir?: string): Promise<SavedAttachment>;
356
- saveEmailAttachments(entryId: string, fileNames: string[], storeId?: string, destDir?: string): Promise<SavedAttachment[]>;
357
- cleanUndeliverableEmails(emailAccount: string, daysBack?: number, dryRun?: boolean): Promise<CleanUndeliverableResult>;
358
- collectBouncedRecipients(emailAccount: string, daysBack?: number, scanDeleted?: boolean): Promise<string[]>;
359
- readSentRecipientGroups(emailAccount: string, daysBack?: number, limit?: number): Promise<SentRecipientGroup[]>;
548
+ locateEmail(description: EmailDescription, options?: LocateEmailOptions): Promise<InboxSearchMatch | null>;
549
+ /** The folders under an account's Inbox. */
550
+ listInboxFolders(emailAccount: string, options?: ListFoldersOptions): Promise<InboxFolderInfo[]>;
551
+ /** File emails into a folder. Moving rewrites each email's entry id. */
552
+ moveOutlookEmails(emailAccount: string, entryIds: readonly string[], folder: string, options?: MoveEmailsOptions): Promise<MoveEmailsResult>;
553
+ /** The mail drafts bound to an account, newest first. */
554
+ listOutlookDrafts(emailAccount: string, options?: ListDraftsOptions): Promise<ListDraftsResult>;
555
+ /** Send the named drafts — each has to be one of this account's drafts. */
556
+ sendDrafts(emailAccount: string, entryIds: readonly string[]): Promise<SendDraftsResult>;
557
+ /** Send every draft bound to an account. */
558
+ sendAllDrafts(emailAccount: string): Promise<SendDraftsResult>;
559
+ /** Delete the named drafts (into Deleted Items) — each has to be one of this account's drafts. */
560
+ deleteOutlookDrafts(emailAccount: string, entryIds: readonly string[]): Promise<DeleteDraftsResult>;
561
+ /** Delete mail by id into Deleted Items, refusing received and sent mail unless allowed. */
562
+ deleteOutlookEmails(emailAccount: string, entryIds: readonly string[], options?: DeleteMailOptions): Promise<DeleteMailResult>;
563
+ /** Permanently empty an account's Deleted Items. The one irreversible operation. */
564
+ purgeDeletedItems(emailAccount: string, options?: PurgeDeletedItemsOptions): Promise<PurgeDeletedItemsResult>;
565
+ /** Save one attachment to disk. */
566
+ saveEmailAttachment(email: EmailRef, fileName: string, options?: SaveAttachmentOptions): Promise<SavedAttachment>;
567
+ /** Save several attachments from one email, in the order named. */
568
+ saveEmailAttachments(email: EmailRef, fileNames: readonly string[], options?: SaveAttachmentOptions): Promise<SavedAttachment[]>;
569
+ /** Find bounce-backs in the Inbox and, unless dry-running, delete them. */
570
+ cleanUndeliverableEmails(emailAccount: string, options?: CleanUndeliverableOptions): Promise<CleanUndeliverableResult>;
571
+ /** The addresses bounce-backs report as having failed, deduplicated. */
572
+ collectBouncedRecipients(emailAccount: string, options?: CollectBouncedRecipientsOptions): Promise<string[]>;
573
+ /** Sent Items, each message with the full set of addresses it went to. */
574
+ readSentRecipientGroups(emailAccount: string, options?: SentRecipientGroupsOptions): Promise<SentRecipientGroup[]>;
575
+ /** Which sent messages bounced, and for which of their recipients. */
576
+ readBounceReport(emailAccount: string, options?: BounceReportOptions): Promise<BounceReport>;
577
+ /** The names of the Outlook signatures on this machine. */
360
578
  listOutlookSignatures(): Promise<string[]>;
579
+ /** A signature's HTML, or '' when there is no signature by that name. */
361
580
  readOutlookSignatureHtml(name: string): Promise<string>;
362
- readTemplateEmails(emailAccount: string, folderName?: string, limit?: number, includeBody?: boolean, subject?: string): Promise<TemplateFolderResult>;
363
- saveTemplateEmail(emailAccount: string, subject: string, htmlBody: string, folderName?: string): Promise<SaveTemplateResult>;
581
+ /** The template emails saved in a mailbox folder. */
582
+ readTemplateEmails(emailAccount: string, options?: ReadTemplatesOptions): Promise<TemplateFolderResult>;
583
+ /** Save a new template email. Always adds; never overwrites. */
584
+ saveTemplateEmail(emailAccount: string, template: SaveTemplateParams): Promise<SaveTemplateResult>;
585
+ /**
586
+ * Open HTML in an Outlook compose window, wait for the person editing it to
587
+ * save and close, and return what they saved. Has no timeout; cancel it
588
+ * with an AbortSignal.
589
+ */
364
590
  editEmailTemplate(label: string, currentHtml: string): Promise<string>;
365
591
  }
366
592
  /** Every operation whose availability can be asked about. */
367
593
  export type BridgeCapability = keyof OutlookBridge;
368
594
  /**
369
- * Which operations actually work on this machine.
370
- *
371
- * The alternative was matching on an error message: a consumer had no way to
372
- * learn a gap existed except by calling into it. A UI can now gray out the
373
- * buttons it can't back rather than discovering the gap when the user clicks one.
374
- *
375
- * Windows and macOS both answer the whole contract today, so every flag is
376
- * `true` on either; the map earns its place on the third case — any other OS,
377
- * where all of them are `false` and every call rejects with
378
- * `UNSUPPORTED_PLATFORM`. `false` is also how a platform would announce the
379
- * softer gap, an operation that answers emptily rather than throwing, so an
380
- * empty result there means "can't" rather than "none found".
381
- *
382
- * Each platform derives its map from its bridge object minus an explicit
383
- * not-ported list (see `shared/capabilities.ts`), and the key type is
384
- * `keyof OutlookBridge`, so a new function in the contract cannot be missed.
595
+ * Which operations work on this machine: all of them on Windows and macOS,
596
+ * none anywhere else. Lets a UI disable what it cannot back instead of
597
+ * discovering the gap when a user clicks.
385
598
  */
386
599
  export type CapabilityMap = Readonly<Record<BridgeCapability, boolean>>;