@dawnlit/outlook-bridge 1.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 (117) hide show
  1. package/LICENSE +190 -0
  2. package/README.md +253 -0
  3. package/dist/OutlookService.d.ts +43 -0
  4. package/dist/OutlookService.js +137 -0
  5. package/dist/OutlookService.js.map +1 -0
  6. package/dist/errors.d.ts +112 -0
  7. package/dist/errors.js +140 -0
  8. package/dist/errors.js.map +1 -0
  9. package/dist/index.d.ts +11 -0
  10. package/dist/index.js +71 -0
  11. package/dist/index.js.map +1 -0
  12. package/dist/mac/accounts.d.ts +5 -0
  13. package/dist/mac/accounts.js +17 -0
  14. package/dist/mac/accounts.js.map +1 -0
  15. package/dist/mac/attachments.d.ts +28 -0
  16. package/dist/mac/attachments.js +120 -0
  17. package/dist/mac/attachments.js.map +1 -0
  18. package/dist/mac/bounces.d.ts +32 -0
  19. package/dist/mac/bounces.js +285 -0
  20. package/dist/mac/bounces.js.map +1 -0
  21. package/dist/mac/cleanup.d.ts +29 -0
  22. package/dist/mac/cleanup.js +219 -0
  23. package/dist/mac/cleanup.js.map +1 -0
  24. package/dist/mac/drafts.d.ts +27 -0
  25. package/dist/mac/drafts.js +214 -0
  26. package/dist/mac/drafts.js.map +1 -0
  27. package/dist/mac/folders.d.ts +28 -0
  28. package/dist/mac/folders.js +127 -0
  29. package/dist/mac/folders.js.map +1 -0
  30. package/dist/mac/index.d.ts +22 -0
  31. package/dist/mac/index.js +119 -0
  32. package/dist/mac/index.js.map +1 -0
  33. package/dist/mac/read.d.ts +46 -0
  34. package/dist/mac/read.js +406 -0
  35. package/dist/mac/read.js.map +1 -0
  36. package/dist/mac/run.d.ts +64 -0
  37. package/dist/mac/run.js +250 -0
  38. package/dist/mac/run.js.map +1 -0
  39. package/dist/mac/scripts.d.ts +137 -0
  40. package/dist/mac/scripts.js +337 -0
  41. package/dist/mac/scripts.js.map +1 -0
  42. package/dist/mac/send.d.ts +23 -0
  43. package/dist/mac/send.js +135 -0
  44. package/dist/mac/send.js.map +1 -0
  45. package/dist/mac/signatures.d.ts +4 -0
  46. package/dist/mac/signatures.js +36 -0
  47. package/dist/mac/signatures.js.map +1 -0
  48. package/dist/mac/templates.d.ts +34 -0
  49. package/dist/mac/templates.js +266 -0
  50. package/dist/mac/templates.js.map +1 -0
  51. package/dist/mail.d.ts +72 -0
  52. package/dist/mail.js +131 -0
  53. package/dist/mail.js.map +1 -0
  54. package/dist/outlookTemplateSections.d.ts +62 -0
  55. package/dist/outlookTemplateSections.js +313 -0
  56. package/dist/outlookTemplateSections.js.map +1 -0
  57. package/dist/runtime.d.ts +76 -0
  58. package/dist/runtime.js +123 -0
  59. package/dist/runtime.js.map +1 -0
  60. package/dist/shared/attachmentMatch.d.ts +32 -0
  61. package/dist/shared/attachmentMatch.js +50 -0
  62. package/dist/shared/attachmentMatch.js.map +1 -0
  63. package/dist/shared/bounceRules.d.ts +33 -0
  64. package/dist/shared/bounceRules.js +104 -0
  65. package/dist/shared/bounceRules.js.map +1 -0
  66. package/dist/shared/capabilities.d.ts +3 -0
  67. package/dist/shared/capabilities.js +9 -0
  68. package/dist/shared/capabilities.js.map +1 -0
  69. package/dist/shared/json.d.ts +11 -0
  70. package/dist/shared/json.js +45 -0
  71. package/dist/shared/json.js.map +1 -0
  72. package/dist/shared/replyBody.d.ts +49 -0
  73. package/dist/shared/replyBody.js +113 -0
  74. package/dist/shared/replyBody.js.map +1 -0
  75. package/dist/types.d.ts +365 -0
  76. package/dist/types.js +13 -0
  77. package/dist/types.js.map +1 -0
  78. package/dist/windows/accounts.d.ts +2 -0
  79. package/dist/windows/accounts.js +22 -0
  80. package/dist/windows/accounts.js.map +1 -0
  81. package/dist/windows/attachments.d.ts +28 -0
  82. package/dist/windows/attachments.js +141 -0
  83. package/dist/windows/attachments.js.map +1 -0
  84. package/dist/windows/bounces.d.ts +34 -0
  85. package/dist/windows/bounces.js +267 -0
  86. package/dist/windows/bounces.js.map +1 -0
  87. package/dist/windows/cleanup.d.ts +29 -0
  88. package/dist/windows/cleanup.js +165 -0
  89. package/dist/windows/cleanup.js.map +1 -0
  90. package/dist/windows/drafts.d.ts +29 -0
  91. package/dist/windows/drafts.js +253 -0
  92. package/dist/windows/drafts.js.map +1 -0
  93. package/dist/windows/folders.d.ts +16 -0
  94. package/dist/windows/folders.js +88 -0
  95. package/dist/windows/folders.js.map +1 -0
  96. package/dist/windows/index.d.ts +27 -0
  97. package/dist/windows/index.js +100 -0
  98. package/dist/windows/index.js.map +1 -0
  99. package/dist/windows/read.d.ts +54 -0
  100. package/dist/windows/read.js +372 -0
  101. package/dist/windows/read.js.map +1 -0
  102. package/dist/windows/run.d.ts +54 -0
  103. package/dist/windows/run.js +161 -0
  104. package/dist/windows/run.js.map +1 -0
  105. package/dist/windows/scripts.d.ts +56 -0
  106. package/dist/windows/scripts.js +163 -0
  107. package/dist/windows/scripts.js.map +1 -0
  108. package/dist/windows/send.d.ts +11 -0
  109. package/dist/windows/send.js +114 -0
  110. package/dist/windows/send.js.map +1 -0
  111. package/dist/windows/signatures.d.ts +14 -0
  112. package/dist/windows/signatures.js +69 -0
  113. package/dist/windows/signatures.js.map +1 -0
  114. package/dist/windows/templates.d.ts +25 -0
  115. package/dist/windows/templates.js +246 -0
  116. package/dist/windows/templates.js.map +1 -0
  117. package/package.json +50 -0
@@ -0,0 +1,365 @@
1
+ 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. */
4
+ emailAccount: string;
5
+ /** One or more addresses, comma- or semicolon-separated. */
6
+ to: string;
7
+ cc?: string;
8
+ subject: string;
9
+ htmlBody: string;
10
+ attachmentPath?: string;
11
+ sendImmediately: boolean;
12
+ /**
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.
16
+ */
17
+ openDraftWindow?: boolean;
18
+ }
19
+ export interface ReplyEmailParams {
20
+ emailAccount: string;
21
+ entryId: string;
22
+ /** StoreID from the same listing row — disambiguates across mailboxes. */
23
+ storeId?: string;
24
+ /**
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.
29
+ */
30
+ 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. */
33
+ templateSubject?: string;
34
+ /** Folder holding the template when templateSubject is used (default 'Templates'). */
35
+ templateFolder?: string;
36
+ /**
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.
40
+ */
41
+ templateSection?: string;
42
+ /** `{{PLACEHOLDER}}` → HTML substituted into the composed body. */
43
+ templatePlaceholders?: Record<string, string>;
44
+ /**
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.
48
+ */
49
+ signatureName?: string;
50
+ /**
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.
54
+ */
55
+ replyAll?: boolean;
56
+ sendImmediately?: boolean;
57
+ openDraftWindow?: boolean;
58
+ }
59
+ export interface ReplyEmailResult {
60
+ to: string;
61
+ subject: string;
62
+ /** The original email's sender — verify it's who you meant to answer. */
63
+ repliedToSender: string;
64
+ }
65
+ /** One draft that couldn't be sent, identified by its subject for the report. */
66
+ export interface DraftSendFailure {
67
+ subject: string;
68
+ error: string;
69
+ }
70
+ export interface SendAllDraftsResult {
71
+ sent: number;
72
+ failed: DraftSendFailure[];
73
+ }
74
+ /** One mail draft belonging to an account. */
75
+ export interface OutlookDraft {
76
+ entryId: string;
77
+ subject: string;
78
+ /** The To line as Outlook renders it (display names). */
79
+ to: string;
80
+ /** Recipient addresses, best-effort — an unresolved Exchange entry falls back to its name. */
81
+ toEmails: string[];
82
+ /** Short plain-text preview — enough to tell one template section from another. */
83
+ bodyPreview: string;
84
+ hasAttachments: boolean;
85
+ lastModified: string;
86
+ /** Which Drafts folder it sits in, since two can hold one account's mail. */
87
+ folderPath: string;
88
+ }
89
+ export interface ListDraftsResult {
90
+ account: string;
91
+ /** The Drafts folders scanned, in scan order. */
92
+ foldersScanned: string[];
93
+ /** Total matching drafts found, before `limit` was applied. */
94
+ count: number;
95
+ truncated: boolean;
96
+ drafts: OutlookDraft[];
97
+ }
98
+ export interface DeleteDraftsResult {
99
+ deleted: number;
100
+ failed: {
101
+ entryId: string;
102
+ error: string;
103
+ }[];
104
+ }
105
+ /** What happened to one id in a deleteOutlookEmails call. */
106
+ export interface DeleteMailOutcome {
107
+ entryId: string;
108
+ subject: string;
109
+ /** The folder the item was actually in — the audit trail for what you deleted. */
110
+ folderPath: string;
111
+ status: 'deleted' | 'would-delete' | 'refused' | 'failed';
112
+ reason: string;
113
+ }
114
+ export interface DeleteMailResult {
115
+ dryRun: boolean;
116
+ deleted: number;
117
+ refused: number;
118
+ failed: number;
119
+ items: DeleteMailOutcome[];
120
+ }
121
+ export interface DeleteMailOptions {
122
+ /** Lift the Inbox/Sent Items refusal, taking responsibility for reaching received mail. */
123
+ allowProtected?: boolean;
124
+ /** Resolve and report without deleting anything. */
125
+ dryRun?: boolean;
126
+ }
127
+ export interface PurgeDeletedItemsResult {
128
+ folderPath: string;
129
+ dryRun: boolean;
130
+ /** Items old enough to qualify. */
131
+ matched: number;
132
+ purged: number;
133
+ /** Items left because they were newer than `olderThanDays`. */
134
+ kept: number;
135
+ failed: number;
136
+ }
137
+ /** One bounce-back / non-delivery message found in the inbox. */
138
+ export interface UndeliverableEmail {
139
+ entryId: string;
140
+ subject: string;
141
+ senderName: string;
142
+ senderEmail: string;
143
+ receivedTime: string;
144
+ /** Which rule flagged it (NDR class, mail-daemon sender, or a bounce subject phrase). */
145
+ matchedReason: string;
146
+ /** Best-effort recipient addresses parsed from the bounce body — which sends failed. */
147
+ failedRecipients: string[];
148
+ }
149
+ export interface CleanUndeliverableResult {
150
+ account: string;
151
+ scannedDays: number;
152
+ /** True when nothing was deleted (preview run). */
153
+ dryRun: boolean;
154
+ matchedCount: number;
155
+ deletedCount: number;
156
+ matched: UndeliverableEmail[];
157
+ /** Matched items that could not be deleted (only populated on a delete run). */
158
+ failed: DraftSendFailure[];
159
+ }
160
+ /** One sent message and the full set of SMTP addresses it went to. */
161
+ export interface SentRecipientGroup {
162
+ entryId: string;
163
+ subject: string;
164
+ sentOn: string;
165
+ recipients: string[];
166
+ }
167
+ export interface InboxEmail {
168
+ entryId: string;
169
+ storeId: string;
170
+ subject: string;
171
+ senderName: string;
172
+ senderEmail: string;
173
+ receivedTime: string;
174
+ bodyPreview: string;
175
+ attachmentNames: string[];
176
+ attachmentCount: number;
177
+ /** Outlook path of the folder each email was read from, so a caller that
178
+ * scoped to a subfolder can confirm which folder actually matched.
179
+ * Populated on both platforms. */
180
+ folderPath?: string;
181
+ }
182
+ export interface InboxSearchFilter {
183
+ /** SQL-LIKE fragment for the [Subject] clause in Items.Restrict — the
184
+ * server-side prefilter that keeps a full-mailbox walk cheap, e.g.
185
+ * '*invoice*'. Omit to restrict on date only. */
186
+ subjectLike?: string;
187
+ /** Regex re-checked client-side against each survivor's trimmed subject,
188
+ * since Restrict's `like` is a blunt substring match. A normal JS
189
+ * RegExp — only its `.source` crosses into the PowerShell/.NET regex
190
+ * engine, which reads the same syntax; `-match` is case-insensitive
191
+ * there by default regardless of the JS pattern's `i` flag. */
192
+ subjectPattern?: RegExp;
193
+ /** Drop subjects starting with Re:/Fw:/Fwd:. */
194
+ excludeReplies?: boolean;
195
+ /** Only return items carrying at least one attachment. */
196
+ requireAttachment?: boolean;
197
+ }
198
+ export interface InboxSearchMatch {
199
+ entryId: string;
200
+ /** Store the item lives in — pass alongside entryId so it resolves unambiguously
201
+ * across mailboxes (replyOutlookEmail, saveEmailAttachments). */
202
+ storeId: string;
203
+ subject: string;
204
+ senderName: string;
205
+ /** SMTP address; resolved from the Exchange DN when the sender is an EX recipient. */
206
+ senderEmail: string;
207
+ /** 'yyyy-MM-dd HH:mm'. */
208
+ receivedTime: string;
209
+ body: string;
210
+ /** Not saved to disk — pass the ones you want through saveEmailAttachments. */
211
+ attachmentNames: string[];
212
+ folderPath: string;
213
+ }
214
+ export interface SelectedEmail {
215
+ entryId: string;
216
+ storeId: string;
217
+ subject: string;
218
+ senderName: string;
219
+ senderEmail: string;
220
+ receivedTime: string;
221
+ body: string;
222
+ /** Not saved to disk — pass the ones you want through saveEmailAttachments. */
223
+ attachmentNames: string[];
224
+ }
225
+ export interface EmailBodyResult {
226
+ entryId: string;
227
+ subject: string;
228
+ senderName: string;
229
+ senderEmail: string;
230
+ receivedTime: string;
231
+ /** The sender's own new text — everything above the quoted thread. */
232
+ body: string;
233
+ /** True when `body` hit maxChars and was cut. */
234
+ truncated: boolean;
235
+ /** Length of the sender's new text before any capping. */
236
+ bodyLength: number;
237
+ /** What the quoted thread was split on ("-----Original Message-----", …); empty when none was found. */
238
+ quoteSeparator: string;
239
+ /** Length of the quoted thread, reported even when it isn't returned. */
240
+ quotedLength: number;
241
+ /** The quoted thread — only populated when the caller asked for it. */
242
+ quotedOriginal: string;
243
+ attachmentNames: string[];
244
+ attachmentCount: number;
245
+ }
246
+ export interface MailFolderRef {
247
+ /** olDefaultFolders id of the root the walk starts from. */
248
+ rootId: number;
249
+ /** That root's name, for error messages. */
250
+ rootLabel: string;
251
+ /** Path segments below the root. */
252
+ segments: string[];
253
+ }
254
+ export interface InboxFolderInfo {
255
+ name: string;
256
+ /** Outlook folder path, e.g. "\\\\mailbox\\Inbox\\Invoices". */
257
+ folderPath: string;
258
+ itemCount: number;
259
+ /** 1 = direct child of Inbox, 2 = grandchild, … */
260
+ depth: number;
261
+ }
262
+ export interface MoveEmailsResult {
263
+ folderPath: string;
264
+ folderCreated: boolean;
265
+ moved: number;
266
+ failed: {
267
+ entryId: string;
268
+ error: string;
269
+ }[];
270
+ }
271
+ export interface SavedAttachment {
272
+ path: string;
273
+ subject: string;
274
+ senderName: string;
275
+ senderEmail: string;
276
+ receivedTime: string;
277
+ }
278
+ /** One template email stored in a mailbox folder. */
279
+ export interface TemplateEmail {
280
+ entryId: string;
281
+ subject: string;
282
+ /** Full HTML body — reusable verbatim as another email's htmlBody. Empty when the
283
+ * listing was requested without bodies (includeBody:false). */
284
+ htmlBody: string;
285
+ /** Short plain-text preview, returned instead of htmlBody when includeBody is false. */
286
+ bodyPreview?: string;
287
+ /** [[SECTION]] names when the template holds several reply variants (see
288
+ * outlookTemplateSections) — returned with or without the body, so a caller can
289
+ * confirm the sections it's about to ask for still exist. */
290
+ sections?: string[];
291
+ /** `{{PLACEHOLDER}}` names the template expects to have filled in. */
292
+ placeholders?: string[];
293
+ lastModified: string;
294
+ }
295
+ export interface TemplateFolderResult {
296
+ folderFound: boolean;
297
+ /** Outlook folder path when found, e.g. "\\\\mailbox\\Templates". */
298
+ folderPath: string;
299
+ templates: TemplateEmail[];
300
+ /** When the folder wasn't found: the mailbox's folder names, so the caller can pick or create one. */
301
+ availableFolders: string[];
302
+ }
303
+ export interface SaveTemplateResult {
304
+ folderPath: string;
305
+ folderCreated: boolean;
306
+ }
307
+ /**
308
+ * What every platform implementation provides. `OutlookService` picks one at load
309
+ * time and exposes it under these exact signatures.
310
+ *
311
+ * Not everything is implemented everywhere — macOS in particular still throws for
312
+ * a good part of this. That is a runtime answer ("not implemented on this
313
+ * platform"), deliberately not a type-level one: a consumer writing against the
314
+ * package should see one signature per function, not a per-platform subset they
315
+ * have to narrow.
316
+ */
317
+ export interface OutlookBridge {
318
+ getOutlookAccounts(): Promise<string[]>;
319
+ sendOutlookEmail(params: SendEmailParams): Promise<void>;
320
+ replyOutlookEmail(params: ReplyEmailParams): Promise<ReplyEmailResult>;
321
+ sendAllDrafts(emailAccount: string): Promise<SendAllDraftsResult>;
322
+ readInboxEmails(emailAccount: string, daysBack?: number, limit?: number, folder?: string): Promise<InboxEmail[]>;
323
+ searchInboxByFilter(emailAccount: string, filter?: InboxSearchFilter, daysBack?: number): Promise<InboxSearchMatch[]>;
324
+ readSelectedEmail(): Promise<SelectedEmail>;
325
+ readEmailBody(entryId: string, storeId?: string, maxChars?: number, includeQuoted?: boolean): Promise<EmailBodyResult>;
326
+ openOutlookEmail(entryId: string): Promise<void>;
327
+ listInboxFolders(emailAccount: string, maxDepth?: number): Promise<InboxFolderInfo[]>;
328
+ moveOutlookEmails(emailAccount: string, entryIds: string[], folderName: string, createIfMissing?: boolean): Promise<MoveEmailsResult>;
329
+ listOutlookDrafts(emailAccount: string, limit?: number, previewChars?: number): Promise<ListDraftsResult>;
330
+ deleteOutlookDrafts(emailAccount: string, entryIds: string[]): Promise<DeleteDraftsResult>;
331
+ deleteOutlookEmails(emailAccount: string, entryIds: string[], options?: DeleteMailOptions): Promise<DeleteMailResult>;
332
+ purgeDeletedItems(emailAccount: string, olderThanDays?: number, dryRun?: boolean): Promise<PurgeDeletedItemsResult>;
333
+ saveEmailAttachment(entryId: string, fileName: string, storeId?: string, destDir?: string): Promise<string>;
334
+ saveEmailAttachmentDetailed(entryId: string, fileName: string, storeId?: string, destDir?: string): Promise<SavedAttachment>;
335
+ saveEmailAttachments(entryId: string, fileNames: string[], storeId?: string, destDir?: string): Promise<SavedAttachment[]>;
336
+ cleanUndeliverableEmails(emailAccount: string, daysBack?: number, dryRun?: boolean): Promise<CleanUndeliverableResult>;
337
+ collectBouncedRecipients(emailAccount: string, daysBack?: number, scanDeleted?: boolean): Promise<string[]>;
338
+ readSentRecipientGroups(emailAccount: string, daysBack?: number, limit?: number): Promise<SentRecipientGroup[]>;
339
+ listOutlookSignatures(): Promise<string[]>;
340
+ readOutlookSignatureHtml(name: string): Promise<string>;
341
+ readTemplateEmails(emailAccount: string, folderName?: string, limit?: number, includeBody?: boolean, subject?: string): Promise<TemplateFolderResult>;
342
+ saveTemplateEmail(emailAccount: string, subject: string, htmlBody: string, folderName?: string): Promise<SaveTemplateResult>;
343
+ editEmailTemplate(label: string, currentHtml: string): Promise<string>;
344
+ }
345
+ /** Every operation whose availability can be asked about. */
346
+ export type BridgeCapability = keyof OutlookBridge;
347
+ /**
348
+ * Which operations actually work on this machine.
349
+ *
350
+ * The alternative was matching on an error message: a consumer had no way to
351
+ * learn a gap existed except by calling into it. A UI can now grey out the
352
+ * buttons it can't back rather than discovering the gap when the user clicks one.
353
+ *
354
+ * Windows and macOS both answer the whole contract today, so every flag is
355
+ * `true` on either; the map earns its place on the third case — any other OS,
356
+ * where all of them are `false` and every call rejects with
357
+ * `UNSUPPORTED_PLATFORM`. `false` is also how a platform would announce the
358
+ * softer gap, an operation that answers emptily rather than throwing, so an
359
+ * empty result there means "can't" rather than "none found".
360
+ *
361
+ * Each platform derives its map from its bridge object minus an explicit
362
+ * not-ported list (see `shared/capabilities.ts`), and the key type is
363
+ * `keyof OutlookBridge`, so a new function in the contract cannot be missed.
364
+ */
365
+ export type CapabilityMap = Readonly<Record<BridgeCapability, boolean>>;
package/dist/types.js ADDED
@@ -0,0 +1,13 @@
1
+ "use strict";
2
+ // The package's data contract, and the interface both platform implementations
3
+ // answer to.
4
+ //
5
+ // These types used to live in the Windows implementation, so macOS imported its
6
+ // own return shapes from it and its stubs re-declared them structurally. The two
7
+ // drifted, and because OutlookService dispatches by picking one implementation or
8
+ // the other, every drift surfaced in the PUBLIC types as a union — `matched:
9
+ // UndeliverableEmail[] | unknown[]` and the like. Both now implement
10
+ // `OutlookBridge` from here, so a drift is a compile error in the package rather
11
+ // than a union in the consumer's editor.
12
+ Object.defineProperty(exports, "__esModule", { value: true });
13
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":";AAAA,+EAA+E;AAC/E,aAAa;AACb,EAAE;AACF,gFAAgF;AAChF,iFAAiF;AACjF,kFAAkF;AAClF,6EAA6E;AAC7E,qEAAqE;AACrE,iFAAiF;AACjF,yCAAyC"}
@@ -0,0 +1,2 @@
1
+ /** Retrieve all Outlook email accounts (SMTP addresses). */
2
+ export declare function getOutlookAccounts(): Promise<string[]>;
@@ -0,0 +1,22 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getOutlookAccounts = getOutlookAccounts;
4
+ const run_1 = require("./run");
5
+ /** Retrieve all Outlook email accounts (SMTP addresses). */
6
+ async function getOutlookAccounts() {
7
+ if (process.platform !== 'win32')
8
+ return [];
9
+ const script = `
10
+ $outlook = New-Object -ComObject Outlook.Application
11
+ $accounts = @()
12
+ foreach ($a in $outlook.Session.Accounts) {
13
+ $accounts += $a.SmtpAddress
14
+ }
15
+ $accounts -join '|'
16
+ `;
17
+ const result = await (0, run_1.runPowerShell)(script);
18
+ if (!result)
19
+ return [];
20
+ return result.split('|').filter(Boolean);
21
+ }
22
+ //# sourceMappingURL=accounts.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"accounts.js","sourceRoot":"","sources":["../../src/windows/accounts.ts"],"names":[],"mappings":";;AAGA,gDAaC;AAhBD,+BAAsC;AAEtC,4DAA4D;AACrD,KAAK,UAAU,kBAAkB;IACpC,IAAI,OAAO,CAAC,QAAQ,KAAK,OAAO;QAAE,OAAO,EAAE,CAAC;IAC5C,MAAM,MAAM,GAAG;;;;;;;CAOlB,CAAC;IACE,MAAM,MAAM,GAAG,MAAM,IAAA,mBAAa,EAAC,MAAM,CAAC,CAAC;IAC3C,IAAI,CAAC,MAAM;QAAE,OAAO,EAAE,CAAC;IACvB,OAAO,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;AAC7C,CAAC"}
@@ -0,0 +1,28 @@
1
+ import type { SavedAttachment } from '../types';
2
+ /**
3
+ * Save an email attachment by entryId and filename, returning the saved path
4
+ * together with the resolved email's subject/sender so the caller can confirm the
5
+ * file came from the email it intended.
6
+ *
7
+ * `destDir` is created if absent. Without one the file lands in a fresh directory
8
+ * of its own, because attachments keep the name the sender gave them: a single
9
+ * shared folder means two emails carrying "invoice.pdf" silently overwrite each
10
+ * other. Pass `destDir` whenever you want the files somewhere you control.
11
+ *
12
+ * When the attachment isn't found, throws an error that names the email actually
13
+ * resolved (from/subject/received) and the attachments it does carry. Because
14
+ * GetItemFromID happily returns a *different* message when handed a stale or wrong
15
+ * EntryID (common when many replies share one subject), a bare "not found" is
16
+ * misleading — this surfaces which email you're really looking at.
17
+ */
18
+ export declare function saveEmailAttachmentDetailed(entryId: string, fileName: string, storeId?: string, destDir?: string): Promise<SavedAttachment>;
19
+ /** Save an attachment and return just the saved file path. */
20
+ export declare function saveEmailAttachment(entryId: string, fileName: string, storeId?: string, destDir?: string): Promise<string>;
21
+ /**
22
+ * Save several attachments from one email in a single COM round trip — what a
23
+ * caller working through searchInboxByFilter/readSelectedEmail results wants,
24
+ * rather than paying a PowerShell process spawn per attachment. Destination,
25
+ * matching and the not-found error follow saveEmailAttachmentDetailed's rules
26
+ * exactly, applied per name; results come back in the same order as `fileNames`.
27
+ */
28
+ export declare function saveEmailAttachments(entryId: string, fileNames: string[], storeId?: string, destDir?: string): Promise<SavedAttachment[]>;
@@ -0,0 +1,141 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.saveEmailAttachmentDetailed = saveEmailAttachmentDetailed;
4
+ exports.saveEmailAttachment = saveEmailAttachment;
5
+ exports.saveEmailAttachments = saveEmailAttachments;
6
+ // Saving attachments off an email, one at a time or in a batch.
7
+ const run_1 = require("./run");
8
+ const scripts_1 = require("./scripts");
9
+ const json_1 = require("../shared/json");
10
+ const attachmentMatch_1 = require("../shared/attachmentMatch");
11
+ const runtime_1 = require("../runtime");
12
+ const errors_1 = require("../errors");
13
+ /**
14
+ * The "not found" throw, built from the shared wording with PowerShell
15
+ * interpolations in place of the values — so the sentence a Windows caller sees
16
+ * is literally the same one macOS composes.
17
+ */
18
+ const NOT_FOUND_THROW = `throw "${(0, attachmentMatch_1.attachmentNotFoundMessage)({
19
+ fileName: '$target',
20
+ senderEmail: '$($item.SenderEmailAddress)',
21
+ subject: '$($item.Subject)',
22
+ receivedTime: '$($item.ReceivedTime)',
23
+ present: '$haveStr',
24
+ idLabel: 'EntryID',
25
+ })}"`;
26
+ /**
27
+ * Locate one attachment by name into `$found`, or throw naming the email that
28
+ * actually resolved. Matching is exact first, then whitespace-normalized (trim +
29
+ * collapse runs, incl. non-breaking spaces) so a trivially reformatted filename
30
+ * still resolves.
31
+ */
32
+ const FIND_ATTACHMENT_PS = `
33
+ $found = $null
34
+ foreach ($att in $item.Attachments) {
35
+ if ($att.FileName -eq $target) { $found = $att; break }
36
+ }
37
+ if ($found -eq $null) {
38
+ $tnorm = ($target -replace '\\s+', ' ').Trim()
39
+ foreach ($att in $item.Attachments) {
40
+ if ((($att.FileName -replace '\\s+', ' ').Trim()) -ieq $tnorm) { $found = $att; break }
41
+ }
42
+ }
43
+ if ($found -eq $null) {
44
+ $have = @(); foreach ($a in $item.Attachments) { $have += $a.FileName }
45
+ $haveStr = if ($have.Count -gt 0) { $have -join ', ' } else { '(none)' }
46
+ ${NOT_FOUND_THROW}
47
+ }
48
+ `;
49
+ /** The row every saver returns: the file, plus who the email was really from. */
50
+ const SAVED_ROW_PS = `[PSCustomObject]@{
51
+ path = $savePath
52
+ subject = if ($item.Subject) { $item.Subject } else { '' }
53
+ senderName = if ($item.SenderName) { $item.SenderName } else { '' }
54
+ senderEmail = if ($item.SenderEmailAddress) { $item.SenderEmailAddress } else { '' }
55
+ receivedTime = if ($item.ReceivedTime) { $item.ReceivedTime.ToString('yyyy-MM-dd HH:mm') } else { '' }
56
+ }`;
57
+ function savedAttachment(value) {
58
+ const e = (0, json_1.record)(value);
59
+ return {
60
+ path: (0, json_1.str)(e.path),
61
+ subject: (0, json_1.str)(e.subject),
62
+ senderName: (0, json_1.str)(e.senderName),
63
+ senderEmail: (0, json_1.str)(e.senderEmail),
64
+ receivedTime: (0, json_1.str)(e.receivedTime),
65
+ };
66
+ }
67
+ /**
68
+ * Save an email attachment by entryId and filename, returning the saved path
69
+ * together with the resolved email's subject/sender so the caller can confirm the
70
+ * file came from the email it intended.
71
+ *
72
+ * `destDir` is created if absent. Without one the file lands in a fresh directory
73
+ * of its own, because attachments keep the name the sender gave them: a single
74
+ * shared folder means two emails carrying "invoice.pdf" silently overwrite each
75
+ * other. Pass `destDir` whenever you want the files somewhere you control.
76
+ *
77
+ * When the attachment isn't found, throws an error that names the email actually
78
+ * resolved (from/subject/received) and the attachments it does carry. Because
79
+ * GetItemFromID happily returns a *different* message when handed a stale or wrong
80
+ * EntryID (common when many replies share one subject), a bare "not found" is
81
+ * misleading — this surfaces which email you're really looking at.
82
+ */
83
+ async function saveEmailAttachmentDetailed(entryId, fileName, storeId, destDir) {
84
+ (0, run_1.requireWindows)();
85
+ const outDir = (0, runtime_1.resolveDestDir)(destDir);
86
+ const script = `
87
+ $outlook = New-Object -ComObject Outlook.Application
88
+ $ns = $outlook.GetNamespace('mapi')
89
+ $ns.Logon()
90
+ ${(0, scripts_1.getItemScript)(entryId, storeId)}
91
+ if ($item -eq $null) { throw "Email not found for EntryID '${(0, run_1.psEscape)(entryId)}'" }
92
+ $target = '${(0, run_1.psEscape)(fileName)}'
93
+ ${FIND_ATTACHMENT_PS}
94
+ $savePath = [IO.Path]::Combine('${(0, run_1.psEscape)(outDir)}', $found.FileName)
95
+ $found.SaveAsFile($savePath)
96
+ $out = ${SAVED_ROW_PS}
97
+ ConvertTo-Json $out -Compress
98
+ `;
99
+ const raw = await (0, run_1.runPowerShell)(script, 15000);
100
+ if (!raw || !raw.trim())
101
+ throw new errors_1.NotFoundError('attachment', 'Failed to save attachment.');
102
+ return savedAttachment((0, json_1.parseObject)(raw));
103
+ }
104
+ /** Save an attachment and return just the saved file path. */
105
+ async function saveEmailAttachment(entryId, fileName, storeId, destDir) {
106
+ return (await saveEmailAttachmentDetailed(entryId, fileName, storeId, destDir)).path;
107
+ }
108
+ /**
109
+ * Save several attachments from one email in a single COM round trip — what a
110
+ * caller working through searchInboxByFilter/readSelectedEmail results wants,
111
+ * rather than paying a PowerShell process spawn per attachment. Destination,
112
+ * matching and the not-found error follow saveEmailAttachmentDetailed's rules
113
+ * exactly, applied per name; results come back in the same order as `fileNames`.
114
+ */
115
+ async function saveEmailAttachments(entryId, fileNames, storeId, destDir) {
116
+ (0, run_1.requireWindows)();
117
+ if (fileNames.length === 0)
118
+ return [];
119
+ const outDir = (0, runtime_1.resolveDestDir)(destDir);
120
+ const script = `
121
+ $outlook = New-Object -ComObject Outlook.Application
122
+ $ns = $outlook.GetNamespace('mapi')
123
+ $ns.Logon()
124
+ ${(0, scripts_1.getItemScript)(entryId, storeId)}
125
+ if ($item -eq $null) { throw "Email not found for EntryID '${(0, run_1.psEscape)(entryId)}'" }
126
+ $targets = @(${(0, run_1.psList)(fileNames)})
127
+ $results = @()
128
+ foreach ($target in $targets) {
129
+ ${FIND_ATTACHMENT_PS}
130
+ $savePath = [IO.Path]::Combine('${(0, run_1.psEscape)(outDir)}', $found.FileName)
131
+ $found.SaveAsFile($savePath)
132
+ $results += ${SAVED_ROW_PS}
133
+ }
134
+ ConvertTo-Json $results -Depth 3 -Compress
135
+ `;
136
+ const raw = await (0, run_1.runPowerShell)(script, 15000);
137
+ if (!raw || !raw.trim())
138
+ throw new errors_1.NotFoundError('attachment', 'Failed to save attachments.');
139
+ return (0, json_1.parseArray)(raw).map(savedAttachment);
140
+ }
141
+ //# sourceMappingURL=attachments.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"attachments.js","sourceRoot":"","sources":["../../src/windows/attachments.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,34 @@
1
+ import type { CleanUndeliverableResult, SentRecipientGroup } from '../types';
2
+ /**
3
+ * Scan an account's inbox for bounce-back / non-delivery messages — Outlook NDRs,
4
+ * mail-daemon/postmaster rejections, and "Message blocked"-style Google/O365
5
+ * failure notices — and, unless previewing, move each to Deleted Items (recoverable).
6
+ *
7
+ * Classification is deliberately conservative so ordinary mail that merely
8
+ * mentions "delivery" is never caught: an item matches only when its
9
+ * MessageClass is an NDR report, its sender fingerprints as a mail-delivery
10
+ * daemon/postmaster, or its subject contains a specific bounce phrase.
11
+ *
12
+ * Matched item references are snapshotted before any Delete() call — deleting
13
+ * mutates the folder collection, so deleting mid-enumeration would skip items
14
+ * (same pattern as sendAllDrafts).
15
+ */
16
+ export declare function cleanUndeliverableEmails(emailAccount: string, daysBack?: number, dryRun?: boolean): Promise<CleanUndeliverableResult>;
17
+ /**
18
+ * Read-only scan of an account's Inbox (and, when scanDeleted, its Deleted Items)
19
+ * for bounce messages, returning just the deduped set of failed recipient
20
+ * addresses. Uses the same conservative classifier as cleanUndeliverableEmails.
21
+ *
22
+ * Deleted Items is included so this still works after cleanUndeliverableEmails has
23
+ * already filed the bounces there — the blacklist step doesn't depend on running
24
+ * before the cleanup. This never deletes anything.
25
+ */
26
+ export declare function collectBouncedRecipients(emailAccount: string, daysBack?: number, scanDeleted?: boolean): Promise<string[]>;
27
+ /**
28
+ * Read the account's Sent Items within the window, returning each mail with the
29
+ * full SMTP address set it was sent to (To + CC + BCC), newest first. This is how
30
+ * the "was every address for this contact tried?" question gets answered: where a
31
+ * blast sends one email per organization addressed to all of its addresses, a sent
32
+ * message's recipient set IS that organization's full address set.
33
+ */
34
+ export declare function readSentRecipientGroups(emailAccount: string, daysBack?: number, limit?: number): Promise<SentRecipientGroup[]>;