@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
@@ -3,30 +3,31 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.cleanUndeliverableEmails = cleanUndeliverableEmails;
4
4
  exports.collectBouncedRecipients = collectBouncedRecipients;
5
5
  exports.readSentRecipientGroups = readSentRecipientGroups;
6
- // Bounce-backs: finding them, filing them away, and mining them for the
6
+ // Bounce-backs: finding them, clearing them away, and mining them for the
7
7
  // addresses that failed.
8
8
  //
9
- // Windows classifies inside the generated PowerShell, where Restrict has already
10
- // narrowed the set server-side. AppleScript has no Restrict, so the folder is
11
- // indexed with bulk property reads and the SAME rules — imported from
12
- // shared/bounceRules — are applied here in TypeScript. Only messages that match
13
- // have their bodies read, which is the property that keeps either version
14
- // affordable on a real inbox.
9
+ // Windows classifies inside PowerShell, after Restrict has narrowed the set.
10
+ // AppleScript has no Restrict, so a folder is indexed with bulk property reads
11
+ // and the SAME rules (shared/bounceRules) run here in TypeScript. Only matches
12
+ // have their bodies read, which keeps either version affordable.
15
13
  const run_1 = require("./run");
16
14
  const scripts_1 = require("./scripts");
17
15
  const bounceRules_1 = require("../shared/bounceRules");
18
- const mail_1 = require("../mail");
19
16
  /**
20
17
  * Index one well-known folder's messages within the window: everything the
21
- * bounce classifier needs, and nothing that costs a per-message event.
18
+ * classifier needs, in bulk reads — `sender of every message` hands back a list
19
+ * of records AppleScript reads locally, so the whole index is five Apple events.
20
+ *
21
+ * The fifth is the header block, which carries the structural bounce signal (see
22
+ * DSN_HEADER_MARKERS). Headers are bulkier than the other properties, so they
23
+ * are tested in AppleScript and cross the boundary as one flag per message
24
+ * rather than as text.
22
25
  */
23
- async function indexFolder(emailAccount, folderTerm, days) {
24
- // Sender is a record, and a bulk `sender of every message` read hands back a
25
- // list of them that AppleScript then reads locally — so the whole index costs
26
- // four Apple events rather than one per message.
27
- const script = `tell application "Microsoft Outlook"
28
- ${(0, scripts_1.accountLookupSnippet)(emailAccount)}
29
- set scanFolder to ${folderTerm} of targetAcct
26
+ async function indexFolder(emailAccount, folderTerm, daysBack) {
27
+ const acct = await (0, scripts_1.resolveMacAccount)(emailAccount);
28
+ const raw = await (0, run_1.runOsaScript)(`tell application "Microsoft Outlook"
29
+ ${(0, scripts_1.accountLookupSnippet)(acct)}
30
+ ${(0, scripts_1.rootFolderSnippet)(acct, folderTerm, 'scanFolder')}
30
31
  set idList to id of every message of scanFolder
31
32
  set subjList to subject of every message of scanFolder
32
33
  try
@@ -39,7 +40,18 @@ ${(0, scripts_1.accountLookupSnippet)(emailAccount)}
39
40
  on error
40
41
  set sndList to {}
41
42
  end try
42
- set cutoff to (current date) - (${days} * days)
43
+ -- Outlook for Mac publishes no MessageClass, so the delivery-status headers
44
+ -- the report arrived with are the structural signal instead. A folder whose
45
+ -- headers can't be read leaves the list empty and every flag false, which
46
+ -- classifies on the sender and subject rules alone — the behaviour before
47
+ -- this read existed, never an error.
48
+ try
49
+ set hdrList to headers of every message of scanFolder
50
+ on error
51
+ set hdrList to {}
52
+ end try
53
+ set hasHeaders to ((count of hdrList) is (count of idList))
54
+ set cutoff to (current date) - (${(0, run_1.asInt)(daysBack)} * days)
43
55
  set out to ""
44
56
  repeat with i from 1 to (count of idList)
45
57
  set stamp to missing value
@@ -56,18 +68,19 @@ ${(0, scripts_1.accountLookupSnippet)(emailAccount)}
56
68
  set sndName to (name of snd) as string
57
69
  end try
58
70
  end if
59
- set out to out & ${(0, run_1.asRow)([
60
- '(item i of idList as string)',
61
- '(item i of subjList)',
62
- 'sndName',
63
- 'sndAddr',
64
- 'my isoDate(stamp)',
65
- ])}
71
+ set isNdr to false
72
+ if hasHeaders then
73
+ try
74
+ set hdrText to (item i of hdrList) as string
75
+ if ${bounceRules_1.DSN_HEADER_MARKERS.map(marker => `hdrText contains ${(0, run_1.asString)(marker)}`).join(' and ')} then set isNdr to true
76
+ end try
77
+ end if
78
+ set out to out & ${(0, run_1.asRow)(['(item i of idList as string)', '(item i of subjList)', 'sndName', 'sndAddr', 'my isoDate(stamp)', '(isNdr as string)'])}
66
79
  end if
67
80
  end repeat
68
81
  return out
69
- end tell`;
70
- return (0, run_1.splitRecords)(await (0, run_1.runOsaScript)(script, 300000)).map(record => {
82
+ end tell`, 'scan');
83
+ return (0, run_1.splitRecords)(raw).map(record => {
71
84
  const parts = (0, run_1.splitFields)(record);
72
85
  return {
73
86
  id: (0, run_1.field)(parts, 0),
@@ -75,16 +88,18 @@ end tell`;
75
88
  senderName: (0, run_1.field)(parts, 2),
76
89
  senderEmail: (0, run_1.field)(parts, 3),
77
90
  receivedTime: (0, run_1.field)(parts, 4),
91
+ isNonDeliveryReport: (0, run_1.boolField)(parts, 5),
78
92
  };
79
93
  });
80
94
  }
81
- /** Read the plain-text bodies of the given messages, keyed by id. */
95
+ /** The plain-text bodies of the given messages, by id. */
82
96
  async function readBodies(ids) {
97
+ const bodies = new Map();
83
98
  if (ids.length === 0)
84
- return new Map();
85
- const script = `tell application "Microsoft Outlook"
99
+ return bodies;
100
+ const raw = await (0, run_1.runOsaScript)(`tell application "Microsoft Outlook"
86
101
  set out to ""
87
- repeat with theId in {${ids.join(', ')}}
102
+ repeat with theId in ${(0, run_1.asIdList)(ids)}
88
103
  set theMsg to missing value
89
104
  try
90
105
  set theMsg to message id theId
@@ -98,9 +113,8 @@ async function readBodies(ids) {
98
113
  end if
99
114
  end repeat
100
115
  return out
101
- end tell`;
102
- const bodies = new Map();
103
- for (const record of (0, run_1.splitRecords)(await (0, run_1.runOsaScript)(script, 300000))) {
116
+ end tell`, 'scan');
117
+ for (const record of (0, run_1.splitRecords)(raw)) {
104
118
  const parts = (0, run_1.splitFields)(record);
105
119
  bodies.set((0, run_1.field)(parts, 0), (0, run_1.field)(parts, 1));
106
120
  }
@@ -125,28 +139,20 @@ async function collectBounces(indexed, accountAddress) {
125
139
  }));
126
140
  }
127
141
  /**
128
- * Scan an account's inbox for bounce-back / non-delivery messages — mail-daemon
129
- * and postmaster rejections, and "Message blocked"-style failure notices — and,
130
- * unless previewing, move each to Deleted Items (recoverable).
131
- *
132
- * Classification is deliberately conservative so ordinary mail that merely
133
- * mentions "delivery" is never caught: an item matches only when its sender
134
- * fingerprints as a mail-delivery daemon/postmaster or its subject contains a
135
- * specific bounce phrase. macOS has no MessageClass, so the NDR-report signal
136
- * Windows can also use is unavailable here — an Exchange NDR still matches on
137
- * its "Undeliverable:" subject, which is what it carries.
142
+ * Find bounce-backs in the Inbox and, unless dry-running, move each to Deleted
143
+ * Items. The same three signals Windows uses apply, in the same order: a report
144
+ * that declares itself one in its headers, then the sender fingerprint, then the
145
+ * subject phrases.
138
146
  */
139
- async function cleanUndeliverableEmails(emailAccount, daysBack = 30, dryRun = true) {
140
- const days = (0, mail_1.clamp)(daysBack, 1, 365);
141
- const indexed = await indexFolder(emailAccount, 'inbox', days);
142
- const matched = await collectBounces(indexed, emailAccount);
147
+ async function cleanUndeliverableEmails(request) {
148
+ const matched = await collectBounces(await indexFolder(request.account, 'inbox', request.daysBack), request.account);
143
149
  let deletedCount = 0;
144
- const failed = [];
145
- if (!dryRun && matched.length > 0) {
146
- const script = `tell application "Microsoft Outlook"
150
+ let failed = [];
151
+ if (!request.dryRun && matched.length > 0) {
152
+ const batch = (0, run_1.splitBatch)(await (0, run_1.runOsaScript)(`tell application "Microsoft Outlook"
147
153
  set deletedCount to 0
148
154
  set out to ""
149
- repeat with theId in {${matched.map(m => m.entryId).join(', ')}}
155
+ repeat with theId in ${(0, run_1.asIdList)(matched.map(m => m.entryId))}
150
156
  set subj to ""
151
157
  try
152
158
  set theMsg to missing value
@@ -160,43 +166,30 @@ async function cleanUndeliverableEmails(emailAccount, daysBack = 30, dryRun = tr
160
166
  delete theMsg
161
167
  set deletedCount to deletedCount + 1
162
168
  on error errText
163
- set out to out & ${(0, run_1.asRow)(['subj', 'errText'])}
169
+ set out to out & ${run_1.ITEM_FAILURE_ROW}
164
170
  end try
165
171
  end repeat
166
172
  return ${(0, run_1.asRow)(['(deletedCount as string)'])} & out
167
- end tell`;
168
- const records = (0, run_1.splitRecords)(await (0, run_1.runOsaScript)(script, 300000));
169
- deletedCount = (0, run_1.intField)((0, run_1.splitFields)(records[0] || ''), 0);
170
- for (const record of records.slice(1)) {
171
- const parts = (0, run_1.splitFields)(record);
172
- failed.push({ subject: (0, run_1.field)(parts, 0), error: (0, run_1.field)(parts, 1) });
173
- }
173
+ end tell`, 'scan'));
174
+ deletedCount = (0, run_1.intField)(batch.summary, 0);
175
+ failed = batch.failed;
174
176
  }
175
177
  return {
176
- account: emailAccount,
177
- scannedDays: days,
178
- dryRun,
178
+ account: request.account,
179
+ scannedDays: request.daysBack,
180
+ dryRun: request.dryRun,
179
181
  matchedCount: matched.length,
180
182
  deletedCount,
181
183
  matched,
182
184
  failed,
183
185
  };
184
186
  }
185
- /**
186
- * Read-only scan of an account's Inbox (and, when scanDeleted, its Deleted Items)
187
- * for bounce messages, returning just the deduped set of failed recipient
188
- * addresses. Uses the same conservative classifier as cleanUndeliverableEmails.
189
- *
190
- * Deleted Items is included so this still works after cleanUndeliverableEmails has
191
- * already filed the bounces there — the blacklist step doesn't depend on running
192
- * before the cleanup. This never deletes anything.
193
- */
194
- async function collectBouncedRecipients(emailAccount, daysBack = 30, scanDeleted = true) {
195
- const days = (0, mail_1.clamp)(daysBack, 1, 365);
196
- const terms = scanDeleted ? ['inbox', 'deleted items'] : ['inbox'];
187
+ /** The deduplicated addresses bounce-backs report as failed. Never deletes anything. */
188
+ async function collectBouncedRecipients(request) {
189
+ const terms = request.includeDeletedItems ? ['inbox', 'deleted items'] : ['inbox'];
197
190
  const found = new Set();
198
191
  for (const term of terms) {
199
- const bounces = await collectBounces(await indexFolder(emailAccount, term, days), emailAccount);
192
+ const bounces = await collectBounces(await indexFolder(request.account, term, request.daysBack), request.account);
200
193
  for (const bounce of bounces) {
201
194
  for (const address of bounce.failedRecipients)
202
195
  found.add(address);
@@ -205,21 +198,15 @@ async function collectBouncedRecipients(emailAccount, daysBack = 30, scanDeleted
205
198
  return [...found];
206
199
  }
207
200
  /**
208
- * Read the account's Sent Items within the window, returning each mail with the
209
- * full SMTP address set it was sent to (To + CC + BCC), newest first. This is how
210
- * the "was every address for this contact tried?" question gets answered: where a
211
- * blast sends one email per organization addressed to all of its addresses, a sent
212
- * message's recipient set IS that organization's full address set.
201
+ * Sent Items within the window, each message with every address it went to,
202
+ * newest first. Indexed on `time sent` — outgoing mail's own stamp; indexing on
203
+ * `time received` yields an empty set that looks exactly like an empty folder.
213
204
  */
214
- async function readSentRecipientGroups(emailAccount, daysBack = 30, limit = 3000) {
215
- const days = (0, mail_1.clamp)(daysBack, 1, 365);
216
- const cap = (0, mail_1.clamp)(limit, 1, 10000);
217
- // Pass 1 — index Sent Items on `time sent`, which is the stamp outgoing mail
218
- // actually carries; indexing it on `time received` yields an empty set that
219
- // looks exactly like an empty folder.
220
- const indexScript = `tell application "Microsoft Outlook"
221
- ${(0, scripts_1.accountLookupSnippet)(emailAccount)}
222
- set sentFolder to sent items of targetAcct
205
+ async function readSentRecipientGroups(request) {
206
+ const acct = await (0, scripts_1.resolveMacAccount)(request.account);
207
+ const indexed = (0, run_1.splitRecords)(await (0, run_1.runOsaScript)(`tell application "Microsoft Outlook"
208
+ ${(0, scripts_1.accountLookupSnippet)(acct)}
209
+ ${(0, scripts_1.rootFolderSnippet)(acct, 'sent items', 'sentFolder')}
223
210
  set idList to id of every message of sentFolder
224
211
  try
225
212
  set timeList to time sent of every message of sentFolder
@@ -227,59 +214,51 @@ ${(0, scripts_1.accountLookupSnippet)(emailAccount)}
227
214
  set timeList to {}
228
215
  end try
229
216
  set subjList to subject of every message of sentFolder
230
- set cutoff to (current date) - (${days} * days)
217
+ set cutoff to (current date) - (${(0, run_1.asInt)(request.daysBack)} * days)
231
218
  set out to ""
232
219
  repeat with i from 1 to (count of idList)
233
220
  set stamp to missing value
234
221
  if (count of timeList) is (count of idList) then set stamp to item i of timeList
235
222
  if stamp is missing value or stamp is greater than or equal to cutoff then
236
- set out to out & ${(0, run_1.asRow)([
237
- '(item i of idList as string)',
238
- '(item i of subjList)',
239
- 'my isoDate(stamp)',
240
- ])}
223
+ set out to out & ${(0, run_1.asRow)(['(item i of idList as string)', '(item i of subjList)', 'my isoDate(stamp)'])}
241
224
  end if
242
225
  end repeat
243
226
  return out
244
- end tell`;
245
- const indexed = (0, run_1.splitRecords)(await (0, run_1.runOsaScript)(indexScript, 300000)).map(record => {
227
+ end tell`, 'scan')).map(record => {
246
228
  const parts = (0, run_1.splitFields)(record);
247
229
  return { id: (0, run_1.field)(parts, 0), subject: (0, run_1.field)(parts, 1).trim(), sentOn: (0, run_1.field)(parts, 2) };
248
230
  });
249
231
  indexed.sort((a, b) => b.sentOn.localeCompare(a.sentOn));
250
- const chosen = indexed.slice(0, cap);
232
+ const chosen = indexed.slice(0, request.limit);
251
233
  if (chosen.length === 0)
252
234
  return [];
253
- // Pass 2 — recipients, which can only be read per message.
254
- const detailScript = `tell application "Microsoft Outlook"
235
+ // Pass 2 — recipients, which can only be read message by message.
236
+ const recipientsById = new Map();
237
+ const raw = await (0, run_1.runOsaScript)(`tell application "Microsoft Outlook"
255
238
  set out to ""
256
- repeat with theId in {${chosen.map(row => row.id).join(', ')}}
239
+ repeat with theId in ${(0, run_1.asIdList)(chosen.map(row => row.id))}
257
240
  set theMsg to missing value
258
241
  try
259
242
  set theMsg to message id theId
260
243
  end try
261
244
  if theMsg is not missing value then
262
- ${(0, scripts_1.allRecipientsSnippet)('theMsg', ' ')}
263
- set out to out & ${(0, run_1.asRow)([
264
- '(theId as string)',
265
- 'my sanitizeList(addrList)',
266
- ])}
245
+ ${(0, scripts_1.allRecipientsSnippet)(' ')}
246
+ set out to out & ${(0, run_1.asRow)(['(theId as string)', 'my sanitizeList(addrList)'])}
267
247
  end if
268
248
  end repeat
269
249
  return out
270
- end tell`;
271
- const recipientsById = new Map();
272
- for (const record of (0, run_1.splitRecords)(await (0, run_1.runOsaScript)(detailScript, 300000))) {
250
+ end tell`, 'scan');
251
+ for (const record of (0, run_1.splitRecords)(raw)) {
273
252
  const parts = (0, run_1.splitFields)(record);
274
253
  recipientsById.set((0, run_1.field)(parts, 0), (0, run_1.splitList)((0, run_1.field)(parts, 1)).map(address => address.toLowerCase()));
275
254
  }
276
- const groups = [];
277
- for (const row of chosen) {
278
- const recipients = recipientsById.get(row.id) || [];
279
- if (recipients.length === 0)
280
- continue;
281
- groups.push({ entryId: row.id, subject: row.subject, sentOn: row.sentOn, recipients });
282
- }
283
- return groups;
255
+ return chosen
256
+ .map(row => ({
257
+ entryId: row.id,
258
+ subject: row.subject,
259
+ sentOn: row.sentOn,
260
+ recipients: recipientsById.get(row.id) ?? [],
261
+ }))
262
+ .filter(group => group.recipients.length > 0);
284
263
  }
285
264
  //# sourceMappingURL=bounces.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"bounces.js","sourceRoot":"","sources":["../../src/mac/bounces.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"bounces.js","sourceRoot":"","sources":["../../src/mac/bounces.ts"],"names":[],"mappings":"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","sourcesContent":["// Bounce-backs: finding them, clearing them away, and mining them for the\n// addresses that failed.\n//\n// Windows classifies inside PowerShell, after Restrict has narrowed the set.\n// AppleScript has no Restrict, so a folder is indexed with bulk property reads\n// and the SAME rules (shared/bounceRules) run here in TypeScript. Only matches\n// have their bodies read, which keeps either version affordable.\nimport {\n asIdList,\n asInt,\n asRow,\n asString,\n boolField,\n field,\n intField,\n ITEM_FAILURE_ROW,\n runOsaScript,\n splitBatch,\n splitFields,\n splitList,\n splitRecords,\n} from './run';\nimport { accountLookupSnippet, allRecipientsSnippet, resolveMacAccount, rootFolderSnippet } from './scripts';\nimport { bounceReason, DSN_HEADER_MARKERS, failedRecipients } from '../shared/bounceRules';\nimport type { CleanUndeliverableRequest, CollectBouncesRequest, SentGroupsRequest } from '../backend';\nimport type { CleanUndeliverableResult, ItemFailure, SentRecipientGroup, UndeliverableEmail } from '../types';\n\ninterface IndexedMessage {\n id: string;\n subject: string;\n senderName: string;\n senderEmail: string;\n receivedTime: string;\n /** Whether the message's own headers declare it a delivery-status notification. */\n isNonDeliveryReport: boolean;\n}\n\n/**\n * Index one well-known folder's messages within the window: everything the\n * classifier needs, in bulk reads — `sender of every message` hands back a list\n * of records AppleScript reads locally, so the whole index is five Apple events.\n *\n * The fifth is the header block, which carries the structural bounce signal (see\n * DSN_HEADER_MARKERS). Headers are bulkier than the other properties, so they\n * are tested in AppleScript and cross the boundary as one flag per message\n * rather than as text.\n */\nasync function indexFolder(emailAccount: string, folderTerm: string, daysBack: number): Promise<IndexedMessage[]> {\n const acct = await resolveMacAccount(emailAccount);\n const raw = await runOsaScript(`tell application \"Microsoft Outlook\"\n${accountLookupSnippet(acct)}\n${rootFolderSnippet(acct, folderTerm, 'scanFolder')}\n set idList to id of every message of scanFolder\n set subjList to subject of every message of scanFolder\n try\n set timeList to time received of every message of scanFolder\n on error\n set timeList to {}\n end try\n try\n set sndList to sender of every message of scanFolder\n on error\n set sndList to {}\n end try\n -- Outlook for Mac publishes no MessageClass, so the delivery-status headers\n -- the report arrived with are the structural signal instead. A folder whose\n -- headers can't be read leaves the list empty and every flag false, which\n -- classifies on the sender and subject rules alone — the behaviour before\n -- this read existed, never an error.\n try\n set hdrList to headers of every message of scanFolder\n on error\n set hdrList to {}\n end try\n set hasHeaders to ((count of hdrList) is (count of idList))\n set cutoff to (current date) - (${asInt(daysBack)} * days)\n set out to \"\"\n repeat with i from 1 to (count of idList)\n set stamp to missing value\n if (count of timeList) is (count of idList) then set stamp to item i of timeList\n if stamp is missing value or stamp is greater than or equal to cutoff then\n set sndName to \"\"\n set sndAddr to \"\"\n if (count of sndList) is (count of idList) then\n set snd to item i of sndList\n try\n set sndAddr to (address of snd) as string\n end try\n try\n set sndName to (name of snd) as string\n end try\n end if\n set isNdr to false\n if hasHeaders then\n try\n set hdrText to (item i of hdrList) as string\n if ${DSN_HEADER_MARKERS.map(marker => `hdrText contains ${asString(marker)}`).join(' and ')} then set isNdr to true\n end try\n end if\n set out to out & ${asRow(['(item i of idList as string)', '(item i of subjList)', 'sndName', 'sndAddr', 'my isoDate(stamp)', '(isNdr as string)'])}\n end if\n end repeat\n return out\nend tell`, 'scan');\n return splitRecords(raw).map(record => {\n const parts = splitFields(record);\n return {\n id: field(parts, 0),\n subject: field(parts, 1).trim(),\n senderName: field(parts, 2),\n senderEmail: field(parts, 3),\n receivedTime: field(parts, 4),\n isNonDeliveryReport: boolField(parts, 5),\n };\n });\n}\n\n/** The plain-text bodies of the given messages, by id. */\nasync function readBodies(ids: readonly string[]): Promise<Map<string, string>> {\n const bodies = new Map<string, string>();\n if (ids.length === 0) return bodies;\n const raw = await runOsaScript(`tell application \"Microsoft Outlook\"\n set out to \"\"\n repeat with theId in ${asIdList(ids)}\n set theMsg to missing value\n try\n set theMsg to message id theId\n end try\n if theMsg is not missing value then\n set bodyText to \"\"\n try\n set bodyText to (plain text content of theMsg) as string\n end try\n set out to out & ${asRow(['(theId as string)', 'bodyText'])}\n end if\n end repeat\n return out\nend tell`, 'scan');\n for (const record of splitRecords(raw)) {\n const parts = splitFields(record);\n bodies.set(field(parts, 0), field(parts, 1));\n }\n return bodies;\n}\n\n/** Classify an index, then fill each match's failed recipients from its body. */\nasync function collectBounces(indexed: readonly IndexedMessage[], accountAddress: string): Promise<UndeliverableEmail[]> {\n const matched = indexed\n .map(message => ({ message, reason: bounceReason(message) }))\n .filter(entry => entry.reason !== '');\n if (matched.length === 0) return [];\n const bodies = await readBodies(matched.map(entry => entry.message.id));\n return matched.map(({ message, reason }) => ({\n entryId: message.id,\n subject: message.subject,\n senderName: message.senderName,\n senderEmail: message.senderEmail,\n receivedTime: message.receivedTime,\n matchedReason: reason,\n failedRecipients: failedRecipients(bodies.get(message.id) || '', accountAddress),\n }));\n}\n\n/**\n * Find bounce-backs in the Inbox and, unless dry-running, move each to Deleted\n * Items. The same three signals Windows uses apply, in the same order: a report\n * that declares itself one in its headers, then the sender fingerprint, then the\n * subject phrases.\n */\nexport async function cleanUndeliverableEmails(request: CleanUndeliverableRequest): Promise<CleanUndeliverableResult> {\n const matched = await collectBounces(await indexFolder(request.account, 'inbox', request.daysBack), request.account);\n let deletedCount = 0;\n let failed: ItemFailure[] = [];\n if (!request.dryRun && matched.length > 0) {\n const batch = splitBatch(await runOsaScript(`tell application \"Microsoft Outlook\"\n set deletedCount to 0\n set out to \"\"\n repeat with theId in ${asIdList(matched.map(m => m.entryId))}\n set subj to \"\"\n try\n set theMsg to missing value\n try\n set theMsg to message id theId\n end try\n if theMsg is missing value then error \"message is no longer in the mailbox\"\n try\n set subj to (subject of theMsg) as string\n end try\n delete theMsg\n set deletedCount to deletedCount + 1\n on error errText\n set out to out & ${ITEM_FAILURE_ROW}\n end try\n end repeat\n return ${asRow(['(deletedCount as string)'])} & out\nend tell`, 'scan'));\n deletedCount = intField(batch.summary, 0);\n failed = batch.failed;\n }\n return {\n account: request.account,\n scannedDays: request.daysBack,\n dryRun: request.dryRun,\n matchedCount: matched.length,\n deletedCount,\n matched,\n failed,\n };\n}\n\n/** The deduplicated addresses bounce-backs report as failed. Never deletes anything. */\nexport async function collectBouncedRecipients(request: CollectBouncesRequest): Promise<string[]> {\n const terms = request.includeDeletedItems ? ['inbox', 'deleted items'] : ['inbox'];\n const found = new Set<string>();\n for (const term of terms) {\n const bounces = await collectBounces(await indexFolder(request.account, term, request.daysBack), request.account);\n for (const bounce of bounces) {\n for (const address of bounce.failedRecipients) found.add(address);\n }\n }\n return [...found];\n}\n\n/**\n * Sent Items within the window, each message with every address it went to,\n * newest first. Indexed on `time sent` — outgoing mail's own stamp; indexing on\n * `time received` yields an empty set that looks exactly like an empty folder.\n */\nexport async function readSentRecipientGroups(request: SentGroupsRequest): Promise<SentRecipientGroup[]> {\n const acct = await resolveMacAccount(request.account);\n const indexed = splitRecords(await runOsaScript(`tell application \"Microsoft Outlook\"\n${accountLookupSnippet(acct)}\n${rootFolderSnippet(acct, 'sent items', 'sentFolder')}\n set idList to id of every message of sentFolder\n try\n set timeList to time sent of every message of sentFolder\n on error\n set timeList to {}\n end try\n set subjList to subject of every message of sentFolder\n set cutoff to (current date) - (${asInt(request.daysBack)} * days)\n set out to \"\"\n repeat with i from 1 to (count of idList)\n set stamp to missing value\n if (count of timeList) is (count of idList) then set stamp to item i of timeList\n if stamp is missing value or stamp is greater than or equal to cutoff then\n set out to out & ${asRow(['(item i of idList as string)', '(item i of subjList)', 'my isoDate(stamp)'])}\n end if\n end repeat\n return out\nend tell`, 'scan')).map(record => {\n const parts = splitFields(record);\n return { id: field(parts, 0), subject: field(parts, 1).trim(), sentOn: field(parts, 2) };\n });\n indexed.sort((a, b) => b.sentOn.localeCompare(a.sentOn));\n const chosen = indexed.slice(0, request.limit);\n if (chosen.length === 0) return [];\n\n // Pass 2 — recipients, which can only be read message by message.\n const recipientsById = new Map<string, string[]>();\n const raw = await runOsaScript(`tell application \"Microsoft Outlook\"\n set out to \"\"\n repeat with theId in ${asIdList(chosen.map(row => row.id))}\n set theMsg to missing value\n try\n set theMsg to message id theId\n end try\n if theMsg is not missing value then\n${allRecipientsSnippet(' ')}\n set out to out & ${asRow(['(theId as string)', 'my sanitizeList(addrList)'])}\n end if\n end repeat\n return out\nend tell`, 'scan');\n for (const record of splitRecords(raw)) {\n const parts = splitFields(record);\n recipientsById.set(field(parts, 0), splitList(field(parts, 1)).map(address => address.toLowerCase()));\n }\n return chosen\n .map(row => ({\n entryId: row.id,\n subject: row.subject,\n sentOn: row.sentOn,\n recipients: recipientsById.get(row.id) ?? [],\n }))\n .filter(group => group.recipients.length > 0);\n}\n"]}
@@ -1,29 +1,15 @@
1
- import type { DeleteMailOptions, DeleteMailResult, PurgeDeletedItemsResult } from '../types';
1
+ import type { DeleteEmailsRequest, PurgeRequest } from '../backend';
2
+ import type { DeleteMailResult, PurgeDeletedItemsResult } from '../types';
2
3
  /**
3
- * Delete mail by message id from anywhere in the account. `delete` files each
4
- * item in Deleted Items, so this is recoverable — purgeDeletedItems is what
5
- * destroys.
6
- *
7
- * ⚠️ This is the one tool here that can reach received mail. Three things hold
8
- * the line, exactly as on Windows:
9
- *
10
- * - **Inbox and Sent Items are refused by default, INCLUDING their subfolders** —
11
- * a filed subfolder is still received mail. `allowProtected` lifts that, and is
12
- * the caller explicitly taking responsibility.
13
- * - **`dryRun` resolves and reports without deleting**, so the exact subjects and
14
- * folders can be shown to the user before anything happens. Use it first.
15
- * - **Every outcome echoes the subject and folderPath** of the item actually
16
- * resolved, so a wrong id is visible after the fact rather than silent.
4
+ * Delete mail by message id from anywhere in the account, into Deleted Items.
5
+ * Inbox and Sent Items (and their subfolders) are refused unless
6
+ * `allowProtected`; `dryRun` reports without deleting; every outcome names the
7
+ * subject and folder of the item that actually resolved.
17
8
  */
18
- export declare function deleteOutlookEmails(emailAccount: string, entryIds: string[], options?: DeleteMailOptions): Promise<DeleteMailResult>;
9
+ export declare function deleteOutlookEmails(request: DeleteEmailsRequest): Promise<DeleteMailResult>;
19
10
  /**
20
- * Permanently remove items from the account's Deleted Items folder. This is the
21
- * ONE genuinely irreversible operation here — nothing recovers from it — which
22
- * is why it is folder-scoped rather than keyed on a message id: it can only ever
23
- * destroy what the user already threw away.
24
- *
25
- * `olderThanDays` keeps recent items (0 = purge everything). The folder is
26
- * indexed first and destroyed by id afterwards, so deleting can't shift the
27
- * collection out from under the walk.
11
+ * Permanently remove items from the account's Deleted Items. The folder is
12
+ * indexed first and items destroyed by id afterwards, so deleting can't shift
13
+ * the collection out from under the walk.
28
14
  */
29
- export declare function purgeDeletedItems(emailAccount: string, olderThanDays?: number, dryRun?: boolean): Promise<PurgeDeletedItemsResult>;
15
+ export declare function purgeDeletedItems(request: PurgeRequest): Promise<PurgeDeletedItemsResult>;