@dawnlit/outlook-bridge 1.1.0 → 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 -36
  39. package/dist/mac/read.js +154 -208
  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 -165
  90. package/dist/types.js +8 -9
  91. package/dist/types.js.map +1 -1
  92. package/dist/windows/accounts.d.ts +10 -1
  93. package/dist/windows/accounts.js +25 -13
  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 +169 -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 +93 -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 +45 -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 -65
  112. package/dist/windows/index.js.map +1 -1
  113. package/dist/windows/read.d.ts +24 -45
  114. package/dist/windows/read.js +266 -279
  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 +71 -38
  120. package/dist/windows/scripts.js +123 -71
  121. package/dist/windows/scripts.js.map +1 -1
  122. package/dist/windows/send.d.ts +6 -11
  123. package/dist/windows/send.js +70 -79
  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 +126 -151
  130. package/dist/windows/templates.js.map +1 -1
  131. package/package.json +5 -4
  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
@@ -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
150
  const failed = [];
145
- if (!dryRun && matched.length > 0) {
146
- const script = `tell application "Microsoft Outlook"
151
+ if (!request.dryRun && matched.length > 0) {
152
+ const raw = 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,34 @@ 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 & ${(0, run_1.asRow)(['(theId as string)', 'subj', 'errText'])}
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);
173
+ end tell`, 'scan');
174
+ const records = (0, run_1.splitRecords)(raw);
175
+ deletedCount = (0, run_1.intField)((0, run_1.splitFields)(records[0] ?? ''), 0);
170
176
  for (const record of records.slice(1)) {
171
177
  const parts = (0, run_1.splitFields)(record);
172
- failed.push({ subject: (0, run_1.field)(parts, 0), error: (0, run_1.field)(parts, 1) });
178
+ failed.push({ entryId: (0, run_1.field)(parts, 0), subject: (0, run_1.field)(parts, 1), error: (0, run_1.field)(parts, 2) });
173
179
  }
174
180
  }
175
181
  return {
176
- account: emailAccount,
177
- scannedDays: days,
178
- dryRun,
182
+ account: request.account,
183
+ scannedDays: request.daysBack,
184
+ dryRun: request.dryRun,
179
185
  matchedCount: matched.length,
180
186
  deletedCount,
181
187
  matched,
182
188
  failed,
183
189
  };
184
190
  }
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'];
191
+ /** The deduplicated addresses bounce-backs report as failed. Never deletes anything. */
192
+ async function collectBouncedRecipients(request) {
193
+ const terms = request.includeDeletedItems ? ['inbox', 'deleted items'] : ['inbox'];
197
194
  const found = new Set();
198
195
  for (const term of terms) {
199
- const bounces = await collectBounces(await indexFolder(emailAccount, term, days), emailAccount);
196
+ const bounces = await collectBounces(await indexFolder(request.account, term, request.daysBack), request.account);
200
197
  for (const bounce of bounces) {
201
198
  for (const address of bounce.failedRecipients)
202
199
  found.add(address);
@@ -205,21 +202,15 @@ async function collectBouncedRecipients(emailAccount, daysBack = 30, scanDeleted
205
202
  return [...found];
206
203
  }
207
204
  /**
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.
205
+ * Sent Items within the window, each message with every address it went to,
206
+ * newest first. Indexed on `time sent` — outgoing mail's own stamp; indexing on
207
+ * `time received` yields an empty set that looks exactly like an empty folder.
213
208
  */
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
209
+ async function readSentRecipientGroups(request) {
210
+ const acct = await (0, scripts_1.resolveMacAccount)(request.account);
211
+ const indexed = (0, run_1.splitRecords)(await (0, run_1.runOsaScript)(`tell application "Microsoft Outlook"
212
+ ${(0, scripts_1.accountLookupSnippet)(acct)}
213
+ ${(0, scripts_1.rootFolderSnippet)(acct, 'sent items', 'sentFolder')}
223
214
  set idList to id of every message of sentFolder
224
215
  try
225
216
  set timeList to time sent of every message of sentFolder
@@ -227,59 +218,51 @@ ${(0, scripts_1.accountLookupSnippet)(emailAccount)}
227
218
  set timeList to {}
228
219
  end try
229
220
  set subjList to subject of every message of sentFolder
230
- set cutoff to (current date) - (${days} * days)
221
+ set cutoff to (current date) - (${(0, run_1.asInt)(request.daysBack)} * days)
231
222
  set out to ""
232
223
  repeat with i from 1 to (count of idList)
233
224
  set stamp to missing value
234
225
  if (count of timeList) is (count of idList) then set stamp to item i of timeList
235
226
  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
- ])}
227
+ set out to out & ${(0, run_1.asRow)(['(item i of idList as string)', '(item i of subjList)', 'my isoDate(stamp)'])}
241
228
  end if
242
229
  end repeat
243
230
  return out
244
- end tell`;
245
- const indexed = (0, run_1.splitRecords)(await (0, run_1.runOsaScript)(indexScript, 300000)).map(record => {
231
+ end tell`, 'scan')).map(record => {
246
232
  const parts = (0, run_1.splitFields)(record);
247
233
  return { id: (0, run_1.field)(parts, 0), subject: (0, run_1.field)(parts, 1).trim(), sentOn: (0, run_1.field)(parts, 2) };
248
234
  });
249
235
  indexed.sort((a, b) => b.sentOn.localeCompare(a.sentOn));
250
- const chosen = indexed.slice(0, cap);
236
+ const chosen = indexed.slice(0, request.limit);
251
237
  if (chosen.length === 0)
252
238
  return [];
253
- // Pass 2 — recipients, which can only be read per message.
254
- const detailScript = `tell application "Microsoft Outlook"
239
+ // Pass 2 — recipients, which can only be read message by message.
240
+ const recipientsById = new Map();
241
+ const raw = await (0, run_1.runOsaScript)(`tell application "Microsoft Outlook"
255
242
  set out to ""
256
- repeat with theId in {${chosen.map(row => row.id).join(', ')}}
243
+ repeat with theId in ${(0, run_1.asIdList)(chosen.map(row => row.id))}
257
244
  set theMsg to missing value
258
245
  try
259
246
  set theMsg to message id theId
260
247
  end try
261
248
  if theMsg is not missing value then
262
249
  ${(0, scripts_1.allRecipientsSnippet)('theMsg', ' ')}
263
- set out to out & ${(0, run_1.asRow)([
264
- '(theId as string)',
265
- 'my sanitizeList(addrList)',
266
- ])}
250
+ set out to out & ${(0, run_1.asRow)(['(theId as string)', 'my sanitizeList(addrList)'])}
267
251
  end if
268
252
  end repeat
269
253
  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))) {
254
+ end tell`, 'scan');
255
+ for (const record of (0, run_1.splitRecords)(raw)) {
273
256
  const parts = (0, run_1.splitFields)(record);
274
257
  recipientsById.set((0, run_1.field)(parts, 0), (0, run_1.splitList)((0, run_1.field)(parts, 1)).map(address => address.toLowerCase()));
275
258
  }
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;
259
+ return chosen
260
+ .map(row => ({
261
+ entryId: row.id,
262
+ subject: row.subject,
263
+ sentOn: row.sentOn,
264
+ recipients: recipientsById.get(row.id) ?? []
265
+ }))
266
+ .filter(group => group.recipients.length > 0);
284
267
  }
285
268
  //# 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 runOsaScript,\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 const failed: ItemFailure[] = [];\n if (!request.dryRun && matched.length > 0) {\n const raw = 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 & ${asRow(['(theId as string)', 'subj', 'errText'])}\n end try\n end repeat\n return ${asRow(['(deletedCount as string)'])} & out\nend tell`, 'scan');\n const records = splitRecords(raw);\n deletedCount = intField(splitFields(records[0] ?? ''), 0);\n for (const record of records.slice(1)) {\n const parts = splitFields(record);\n failed.push({entryId: field(parts, 0), subject: field(parts, 1), error: field(parts, 2)});\n }\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('theMsg', ' ')}\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>;