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