@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
@@ -7,160 +7,146 @@ const run_1 = require("./run");
7
7
  const scripts_1 = require("./scripts");
8
8
  const json_1 = require("../shared/json");
9
9
  const mail_1 = require("../mail");
10
- /** Folders whose contents are received or already-sent mail, not working state. */
11
- const PROTECTED_FOLDER_IDS = [6, 5];
12
- /** Walks an item's parent chain, so Inbox/Sent protection covers their subfolders too. */
13
- const FOLDER_CHAIN_PS = `
14
- function Get-FolderChainIds($folder) {
15
- $ids = @()
16
- $cur = $folder
17
- $guard = 0
18
- while ($cur -ne $null -and $guard -lt 25) {
19
- $cid = $null
20
- try { $cid = $cur.EntryID } catch { $cid = $null }
21
- if (-not $cid) { break }
22
- $ids += $cid
23
- $parent = $null
24
- try { $parent = $cur.Parent } catch { $parent = $null }
25
- if ($parent -eq $null) { break }
26
- $cur = $parent
27
- $guard++
28
- }
29
- return $ids
30
- }
31
- `;
10
+ /** Folders whose contents are received or sent mail rather than working state. */
11
+ const PROTECTED_FOLDER_IDS = [mail_1.FolderId.Inbox, mail_1.FolderId.SentMail];
32
12
  /**
33
- * Delete mail by EntryID from anywhere in the account. Delete() moves each item to
34
- * Deleted Items, so this is recoverable — purgeDeletedItems is what destroys.
13
+ * Delete mail by entry id from anywhere in the account. Delete() moves each item
14
+ * to Deleted Items, so this is recoverable — purgeDeletedItems is what destroys.
35
15
  *
36
- * ⚠️ This is the one tool here that can reach received mail, and EntryIDs are a
37
- * genuinely unsafe key for it: GetItemFromID silently returns a DIFFERENT message
38
- * when handed a stale or wrong id, which is common precisely when many replies share
39
- * one subject (see saveEmailAttachmentDetailed). Three things hold the line:
40
- *
41
- * - **Inbox and Sent Items are refused by default, INCLUDING their subfolders** —
42
- * a filed subfolder is still received mail. `allowProtected` lifts that, and is
43
- * the caller explicitly taking responsibility.
44
- * - **`dryRun` resolves and reports without deleting**, so the exact subjects and
45
- * folders can be shown to the user before anything happens. Use it first.
46
- * - **Every outcome echoes the subject and folderPath** of the item actually
47
- * resolved, so a wrong id is visible after the fact rather than silent.
16
+ * This is the one operation that can reach received mail, and an entry id is an
17
+ * unsafe key for it: a stale or wrong id can resolve to a DIFFERENT message.
18
+ * Four things hold the line:
19
+ * - An id that resolves outside this account's store is refused. The bare
20
+ * lookup falls back to the default store, and the Inbox/Sent check below
21
+ * only knows this account's folders, so another mailbox's received mail
22
+ * would otherwise pass it.
23
+ * - Inbox and Sent Items are refused, INCLUDING their subfolders (a filed
24
+ * folder is still received mail), unless `allowProtected`.
25
+ * - `dryRun` resolves and reports without deleting, so the exact subjects and
26
+ * folders can be checked before anything happens.
27
+ * - Every outcome echoes the subject and folder of the item actually resolved.
48
28
  */
49
- async function deleteOutlookEmails(emailAccount, entryIds, options = {}) {
50
- (0, run_1.requireWindows)();
51
- const { allowProtected = false, dryRun = false } = options;
52
- if (entryIds.length === 0) {
53
- return { dryRun, deleted: 0, refused: 0, failed: 0, items: [] };
54
- }
55
- const script = `${(0, scripts_1.accountScript)(emailAccount)}
29
+ async function deleteOutlookEmails(request) {
30
+ const output = await (0, run_1.runPowerShellJson)(`${(0, scripts_1.accountScript)(request.account)}
56
31
  ${scripts_1.DELIVERY_STORE_PS}
57
- ${FOLDER_CHAIN_PS}
32
+ $allowProtected = ${(0, run_1.psBool)(request.allowProtected)}
33
+ $dryRun = ${(0, run_1.psBool)(request.dryRun)}
34
+ $protectedReason = ${(0, run_1.psString)(mail_1.PROTECTED_MAIL_REASON)}
58
35
  $protectedIds = @{}
59
- foreach ($fid in @(${PROTECTED_FOLDER_IDS.join(',')})) {
60
- try { $protectedIds[$store.GetDefaultFolder($fid).EntryID] = $true } catch {}
36
+ foreach ($role in @(${PROTECTED_FOLDER_IDS.map(run_1.psInt).join(', ')})) {
37
+ try { $protectedIds[[string]$store.GetDefaultFolder($role).EntryID] = $true } catch {}
38
+ }
39
+ function Test-UnderProtectedFolder($folder) {
40
+ $current = $folder
41
+ for ($guard = 0; $current -ne $null -and $guard -lt 25; $guard++) {
42
+ $folderId = $null
43
+ try { $folderId = [string]$current.EntryID } catch {}
44
+ if (-not $folderId) { return $false }
45
+ if ($protectedIds.ContainsKey($folderId)) { return $true }
46
+ $parent = $null
47
+ try { $parent = $current.Parent } catch {}
48
+ $current = $parent
49
+ }
50
+ return $false
51
+ }
52
+ function Test-InAccountStore($folder) {
53
+ $folderStoreId = $null
54
+ try { $folderStoreId = [string]$folder.StoreID } catch {}
55
+ if (-not $folderStoreId) { return $false }
56
+ if ($folderStoreId -ieq $storeId) { return $true }
57
+ # One store can be named by two id strings; MAPI decides whether they match.
58
+ try { return [bool]$ns.CompareEntryIDs($folderStoreId, $storeId) } catch { return $false }
61
59
  }
62
-
63
60
  $items = @()
64
- $deleted = 0
65
- $refused = 0
66
- $failed = 0
67
- foreach ($id in @(${(0, run_1.psList)(entryIds)})) {
61
+ foreach ($id in ${(0, run_1.psArray)(request.entryIds)}) {
68
62
  $subject = ''
69
63
  $folderPath = ''
70
64
  try {
71
65
  $it = $null
72
66
  try { $it = $ns.GetItemFromID($id, $store.StoreID) } catch { $it = $null }
73
67
  if ($it -eq $null) { try { $it = $ns.GetItemFromID($id) } catch { $it = $null } }
74
- if ($it -eq $null) { throw "no item with that EntryID in this account" }
75
- try { $subject = $it.Subject } catch {}
68
+ if ($it -eq $null) { throw "no item with that entry id in this account" }
69
+ try { $subject = [string]$it.Subject } catch {}
76
70
  $parent = $null
77
71
  try { $parent = $it.Parent } catch {}
78
72
  if ($parent -eq $null) { throw "item has no parent folder - refusing to delete" }
79
- try { $folderPath = $parent.FolderPath } catch {}
80
-
81
- $chain = Get-FolderChainIds $parent
82
- $isProtected = $false
83
- foreach ($cid in $chain) { if ($protectedIds.ContainsKey($cid)) { $isProtected = $true; break } }
84
-
85
- if ($isProtected -and -not ${(0, run_1.psBool)(allowProtected)}) {
86
- $refused++
87
- $items += [pscustomobject]@{ entryId = $id; subject = $subject; folderPath = $folderPath; status = 'refused'; reason = '${(0, run_1.psEscape)(mail_1.PROTECTED_MAIL_REASON)}' }
88
- } elseif (${(0, run_1.psBool)(dryRun)}) {
89
- $items += [pscustomobject]@{ entryId = $id; subject = $subject; folderPath = $folderPath; status = 'would-delete'; reason = '' }
73
+ try { $folderPath = [string]$parent.FolderPath } catch {}
74
+ if (-not (Test-InAccountStore $parent)) { throw "item is in another mailbox, not this account - refusing to delete" }
75
+ if ((Test-UnderProtectedFolder $parent) -and -not $allowProtected) {
76
+ $items += [PSCustomObject]@{ entryId = $id; subject = $subject; folderPath = $folderPath; status = 'refused'; reason = $protectedReason }
77
+ } elseif ($dryRun) {
78
+ $items += [PSCustomObject]@{ entryId = $id; subject = $subject; folderPath = $folderPath; status = 'would-delete'; reason = '' }
90
79
  } else {
91
80
  $it.Delete()
92
- $deleted++
93
- $items += [pscustomobject]@{ entryId = $id; subject = $subject; folderPath = $folderPath; status = 'deleted'; reason = '' }
81
+ $items += [PSCustomObject]@{ entryId = $id; subject = $subject; folderPath = $folderPath; status = 'deleted'; reason = '' }
94
82
  }
95
83
  } catch {
96
- $failed++
97
- $items += [pscustomobject]@{ entryId = $id; subject = $subject; folderPath = $folderPath; status = 'failed'; reason = $_.Exception.Message }
84
+ $items += [PSCustomObject]@{ entryId = $id; subject = $subject; folderPath = $folderPath; status = 'failed'; reason = $_.Exception.Message }
98
85
  }
99
86
  }
100
- ConvertTo-Json @{ dryRun = ${(0, run_1.psBool)(dryRun)}; deleted = $deleted; refused = $refused; failed = $failed; items = @($items) } -Depth 4
101
- `;
102
- const parsed = (0, json_1.parseObject)(await (0, run_1.runPowerShell)(script, 300000));
103
- return {
104
- dryRun,
105
- deleted: (0, json_1.num)(parsed.deleted),
106
- refused: (0, json_1.num)(parsed.refused),
107
- failed: (0, json_1.num)(parsed.failed),
108
- items: (0, json_1.toArray)(parsed.items).map(i => {
109
- const e = (0, json_1.record)(i);
110
- return {
111
- entryId: (0, json_1.str)(e.entryId),
112
- subject: (0, json_1.str)(e.subject),
113
- folderPath: (0, json_1.str)(e.folderPath),
114
- status: ((0, json_1.str)(e.status) || 'failed'),
115
- reason: (0, json_1.str)(e.reason),
116
- };
117
- }),
118
- };
87
+ ConvertTo-Json -Depth 3 -InputObject @($items)
88
+ `, 'scan');
89
+ const items = (0, json_1.toArray)(output).map(row => {
90
+ const e = (0, json_1.record)(row);
91
+ return {
92
+ entryId: (0, json_1.str)(e.entryId),
93
+ subject: (0, json_1.str)(e.subject),
94
+ folderPath: (0, json_1.str)(e.folderPath),
95
+ status: (0, mail_1.deleteStatus)((0, json_1.str)(e.status)),
96
+ reason: (0, json_1.str)(e.reason),
97
+ };
98
+ });
99
+ return (0, mail_1.deleteMailResult)(request.dryRun, items);
119
100
  }
120
101
  /**
121
- * Permanently remove items from the account's Deleted Items folder. This is the ONE
122
- * genuinely irreversible operation here — nothing recovers from it — which is why it
123
- * is folder-scoped rather than keyed on an EntryID: it can only ever destroy what the
124
- * user already threw away.
102
+ * Permanently remove items from the account's Deleted Items. The ONE irreversible
103
+ * operation here, which is why it is folder-scoped rather than keyed on ids: it
104
+ * can only ever destroy what was already thrown away.
125
105
  *
126
- * `olderThanDays` keeps recent items (0 = purge everything). Iterates backwards, as
127
- * deleting mutates the collection and a forward walk would skip every other item.
106
+ * Walks backwards, since deleting shifts the collection and a forward walk would
107
+ * skip every other item.
128
108
  */
129
- async function purgeDeletedItems(emailAccount, olderThanDays = 0, dryRun = false) {
130
- (0, run_1.requireWindows)();
131
- const script = `${(0, scripts_1.accountScript)(emailAccount)}
109
+ async function purgeDeletedItems(request) {
110
+ const output = await (0, run_1.runPowerShellJson)(`${(0, scripts_1.accountScript)(request.account)}
132
111
  ${scripts_1.DELIVERY_STORE_PS}
133
- $folder = $store.GetDefaultFolder(3)
134
- $cutoff = (Get-Date).AddDays(-${olderThanDays})
112
+ $folder = $store.GetDefaultFolder(${(0, run_1.psInt)(mail_1.FolderId.DeletedItems)})
113
+ $olderThanDays = ${(0, run_1.psInt)(request.olderThanDays)}
114
+ $dryRun = ${(0, run_1.psBool)(request.dryRun)}
115
+ ${(0, scripts_1.cutoffScript)(request.olderThanDays)}
135
116
  $matched = 0
136
117
  $purged = 0
137
118
  $kept = 0
138
119
  $failed = 0
139
- # Bound once: every $folder.Items is a COM property get that materialises a
140
- # fresh collection object, and this loop runs once per message in Deleted Items.
120
+ # Bound once: each $folder.Items is a COM call that builds a fresh collection.
141
121
  $folderItems = $folder.Items
142
122
  for ($i = $folderItems.Count; $i -ge 1; $i--) {
143
123
  $it = $null
144
124
  try { $it = $folderItems.Item($i) } catch { continue }
145
125
  $stamp = $null
146
- foreach ($p in @('ReceivedTime','LastModificationTime','CreationTime')) {
147
- try { $stamp = $it.$p; if ($stamp -ne $null) { break } } catch {}
126
+ foreach ($property in @('ReceivedTime', 'LastModificationTime', 'CreationTime')) {
127
+ try { $stamp = $it.$property; if ($stamp -ne $null) { break } } catch {}
148
128
  }
149
- if (${olderThanDays} -gt 0 -and $stamp -ne $null -and $stamp -gt $cutoff) { $kept++; continue }
129
+ if ($olderThanDays -gt 0 -and $stamp -ne $null -and $stamp -gt $cutoff) { $kept++; continue }
150
130
  $matched++
151
- if (${(0, run_1.psBool)(dryRun)}) { continue }
131
+ if ($dryRun) { continue }
152
132
  try { $it.Delete(); $purged++ } catch { $failed++ }
153
133
  }
154
- ConvertTo-Json @{ folderPath = $folder.FolderPath; matched = $matched; purged = $purged; kept = $kept; failed = $failed } -Depth 3
155
- `;
156
- const parsed = (0, json_1.parseObject)(await (0, run_1.runPowerShell)(script, 600000));
134
+ ConvertTo-Json -Compress -InputObject ([PSCustomObject]@{
135
+ folderPath = [string]$folder.FolderPath
136
+ matched = $matched
137
+ purged = $purged
138
+ kept = $kept
139
+ failed = $failed
140
+ })
141
+ `, 'purge');
142
+ const e = (0, json_1.record)(output);
157
143
  return {
158
- folderPath: (0, json_1.str)(parsed.folderPath),
159
- dryRun,
160
- matched: (0, json_1.num)(parsed.matched),
161
- purged: (0, json_1.num)(parsed.purged),
162
- kept: (0, json_1.num)(parsed.kept),
163
- failed: (0, json_1.num)(parsed.failed),
144
+ folderPath: (0, json_1.str)(e.folderPath),
145
+ dryRun: request.dryRun,
146
+ matched: (0, json_1.num)(e.matched),
147
+ purged: (0, json_1.num)(e.purged),
148
+ kept: (0, json_1.num)(e.kept),
149
+ failed: (0, json_1.num)(e.failed),
164
150
  };
165
151
  }
166
152
  //# sourceMappingURL=cleanup.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"cleanup.js","sourceRoot":"","sources":["../../src/windows/cleanup.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"cleanup.js","sourceRoot":"","sources":["../../src/windows/cleanup.ts"],"names":[],"mappings":";;AA4BA,kDAuEC;AAUD,8CA0CC;AAvJD,wDAAwD;AACxD,+BAA4E;AAC5E,uCAA2E;AAC3E,yCAA2D;AAC3D,kCAA0F;AAI1F,kFAAkF;AAClF,MAAM,oBAAoB,GAAG,CAAC,eAAQ,CAAC,KAAK,EAAE,eAAQ,CAAC,QAAQ,CAAC,CAAC;AAEjE;;;;;;;;;;;;;;;;GAgBG;AACI,KAAK,UAAU,mBAAmB,CAAC,OAA4B;IAClE,MAAM,MAAM,GAAG,MAAM,IAAA,uBAAiB,EAAC,GAAG,IAAA,uBAAa,EAAC,OAAO,CAAC,OAAO,CAAC;EAC1E,2BAAiB;oBACC,IAAA,YAAM,EAAC,OAAO,CAAC,cAAc,CAAC;YACtC,IAAA,YAAM,EAAC,OAAO,CAAC,MAAM,CAAC;qBACb,IAAA,cAAQ,EAAC,4BAAqB,CAAC;;sBAE9B,oBAAoB,CAAC,GAAG,CAAC,WAAK,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;kBAyB9C,IAAA,aAAO,EAAC,OAAO,CAAC,QAAQ,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;CA2B1C,EAAE,MAAM,CAAC,CAAC;IACP,MAAM,KAAK,GAAwB,IAAA,cAAO,EAAC,MAAM,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE;QACzD,MAAM,CAAC,GAAG,IAAA,aAAM,EAAC,GAAG,CAAC,CAAC;QACtB,OAAO;YACH,OAAO,EAAE,IAAA,UAAG,EAAC,CAAC,CAAC,OAAO,CAAC;YACvB,OAAO,EAAE,IAAA,UAAG,EAAC,CAAC,CAAC,OAAO,CAAC;YACvB,UAAU,EAAE,IAAA,UAAG,EAAC,CAAC,CAAC,UAAU,CAAC;YAC7B,MAAM,EAAE,IAAA,mBAAY,EAAC,IAAA,UAAG,EAAC,CAAC,CAAC,MAAM,CAAC,CAAC;YACnC,MAAM,EAAE,IAAA,UAAG,EAAC,CAAC,CAAC,MAAM,CAAC;SACxB,CAAC;IACN,CAAC,CAAC,CAAC;IACH,OAAO,IAAA,uBAAgB,EAAC,OAAO,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;AACnD,CAAC;AAED;;;;;;;GAOG;AACI,KAAK,UAAU,iBAAiB,CAAC,OAAqB;IACzD,MAAM,MAAM,GAAG,MAAM,IAAA,uBAAiB,EAAC,GAAG,IAAA,uBAAa,EAAC,OAAO,CAAC,OAAO,CAAC;EAC1E,2BAAiB;oCACiB,IAAA,WAAK,EAAC,eAAQ,CAAC,YAAY,CAAC;mBAC7C,IAAA,WAAK,EAAC,OAAO,CAAC,aAAa,CAAC;YACnC,IAAA,YAAM,EAAC,OAAO,CAAC,MAAM,CAAC;EAChC,IAAA,sBAAY,EAAC,OAAO,CAAC,aAAa,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;CA0BpC,EAAE,OAAO,CAAC,CAAC;IACR,MAAM,CAAC,GAAG,IAAA,aAAM,EAAC,MAAM,CAAC,CAAC;IACzB,OAAO;QACH,UAAU,EAAE,IAAA,UAAG,EAAC,CAAC,CAAC,UAAU,CAAC;QAC7B,MAAM,EAAE,OAAO,CAAC,MAAM;QACtB,OAAO,EAAE,IAAA,UAAG,EAAC,CAAC,CAAC,OAAO,CAAC;QACvB,MAAM,EAAE,IAAA,UAAG,EAAC,CAAC,CAAC,MAAM,CAAC;QACrB,IAAI,EAAE,IAAA,UAAG,EAAC,CAAC,CAAC,IAAI,CAAC;QACjB,MAAM,EAAE,IAAA,UAAG,EAAC,CAAC,CAAC,MAAM,CAAC;KACxB,CAAC;AACN,CAAC","sourcesContent":["// Deleting mail, and emptying what was already deleted.\nimport { psArray, psBool, psInt, psString, runPowerShellJson } from './run';\nimport { accountScript, cutoffScript, DELIVERY_STORE_PS } from './scripts';\nimport { num, record, str, toArray } from '../shared/json';\nimport { deleteMailResult, deleteStatus, FolderId, PROTECTED_MAIL_REASON } from '../mail';\nimport type { DeleteEmailsRequest, PurgeRequest } from '../backend';\nimport type { DeleteMailOutcome, DeleteMailResult, PurgeDeletedItemsResult } from '../types';\n\n/** Folders whose contents are received or sent mail rather than working state. */\nconst PROTECTED_FOLDER_IDS = [FolderId.Inbox, FolderId.SentMail];\n\n/**\n * Delete mail by entry id from anywhere in the account. Delete() moves each item\n * to Deleted Items, so this is recoverable — purgeDeletedItems is what destroys.\n *\n * This is the one operation that can reach received mail, and an entry id is an\n * unsafe key for it: a stale or wrong id can resolve to a DIFFERENT message.\n * Four things hold the line:\n * - An id that resolves outside this account's store is refused. The bare\n * lookup falls back to the default store, and the Inbox/Sent check below\n * only knows this account's folders, so another mailbox's received mail\n * would otherwise pass it.\n * - Inbox and Sent Items are refused, INCLUDING their subfolders (a filed\n * folder is still received mail), unless `allowProtected`.\n * - `dryRun` resolves and reports without deleting, so the exact subjects and\n * folders can be checked before anything happens.\n * - Every outcome echoes the subject and folder of the item actually resolved.\n */\nexport async function deleteOutlookEmails(request: DeleteEmailsRequest): Promise<DeleteMailResult> {\n const output = await runPowerShellJson(`${accountScript(request.account)}\n${DELIVERY_STORE_PS}\n$allowProtected = ${psBool(request.allowProtected)}\n$dryRun = ${psBool(request.dryRun)}\n$protectedReason = ${psString(PROTECTED_MAIL_REASON)}\n$protectedIds = @{}\nforeach ($role in @(${PROTECTED_FOLDER_IDS.map(psInt).join(', ')})) {\n try { $protectedIds[[string]$store.GetDefaultFolder($role).EntryID] = $true } catch {}\n}\nfunction Test-UnderProtectedFolder($folder) {\n $current = $folder\n for ($guard = 0; $current -ne $null -and $guard -lt 25; $guard++) {\n $folderId = $null\n try { $folderId = [string]$current.EntryID } catch {}\n if (-not $folderId) { return $false }\n if ($protectedIds.ContainsKey($folderId)) { return $true }\n $parent = $null\n try { $parent = $current.Parent } catch {}\n $current = $parent\n }\n return $false\n}\nfunction Test-InAccountStore($folder) {\n $folderStoreId = $null\n try { $folderStoreId = [string]$folder.StoreID } catch {}\n if (-not $folderStoreId) { return $false }\n if ($folderStoreId -ieq $storeId) { return $true }\n # One store can be named by two id strings; MAPI decides whether they match.\n try { return [bool]$ns.CompareEntryIDs($folderStoreId, $storeId) } catch { return $false }\n}\n$items = @()\nforeach ($id in ${psArray(request.entryIds)}) {\n $subject = ''\n $folderPath = ''\n try {\n $it = $null\n try { $it = $ns.GetItemFromID($id, $store.StoreID) } catch { $it = $null }\n if ($it -eq $null) { try { $it = $ns.GetItemFromID($id) } catch { $it = $null } }\n if ($it -eq $null) { throw \"no item with that entry id in this account\" }\n try { $subject = [string]$it.Subject } catch {}\n $parent = $null\n try { $parent = $it.Parent } catch {}\n if ($parent -eq $null) { throw \"item has no parent folder - refusing to delete\" }\n try { $folderPath = [string]$parent.FolderPath } catch {}\n if (-not (Test-InAccountStore $parent)) { throw \"item is in another mailbox, not this account - refusing to delete\" }\n if ((Test-UnderProtectedFolder $parent) -and -not $allowProtected) {\n $items += [PSCustomObject]@{ entryId = $id; subject = $subject; folderPath = $folderPath; status = 'refused'; reason = $protectedReason }\n } elseif ($dryRun) {\n $items += [PSCustomObject]@{ entryId = $id; subject = $subject; folderPath = $folderPath; status = 'would-delete'; reason = '' }\n } else {\n $it.Delete()\n $items += [PSCustomObject]@{ entryId = $id; subject = $subject; folderPath = $folderPath; status = 'deleted'; reason = '' }\n }\n } catch {\n $items += [PSCustomObject]@{ entryId = $id; subject = $subject; folderPath = $folderPath; status = 'failed'; reason = $_.Exception.Message }\n }\n}\nConvertTo-Json -Depth 3 -InputObject @($items)\n`, 'scan');\n const items: DeleteMailOutcome[] = toArray(output).map(row => {\n const e = record(row);\n return {\n entryId: str(e.entryId),\n subject: str(e.subject),\n folderPath: str(e.folderPath),\n status: deleteStatus(str(e.status)),\n reason: str(e.reason),\n };\n });\n return deleteMailResult(request.dryRun, items);\n}\n\n/**\n * Permanently remove items from the account's Deleted Items. The ONE irreversible\n * operation here, which is why it is folder-scoped rather than keyed on ids: it\n * can only ever destroy what was already thrown away.\n *\n * Walks backwards, since deleting shifts the collection and a forward walk would\n * skip every other item.\n */\nexport async function purgeDeletedItems(request: PurgeRequest): Promise<PurgeDeletedItemsResult> {\n const output = await runPowerShellJson(`${accountScript(request.account)}\n${DELIVERY_STORE_PS}\n$folder = $store.GetDefaultFolder(${psInt(FolderId.DeletedItems)})\n$olderThanDays = ${psInt(request.olderThanDays)}\n$dryRun = ${psBool(request.dryRun)}\n${cutoffScript(request.olderThanDays)}\n$matched = 0\n$purged = 0\n$kept = 0\n$failed = 0\n# Bound once: each $folder.Items is a COM call that builds a fresh collection.\n$folderItems = $folder.Items\nfor ($i = $folderItems.Count; $i -ge 1; $i--) {\n $it = $null\n try { $it = $folderItems.Item($i) } catch { continue }\n $stamp = $null\n foreach ($property in @('ReceivedTime', 'LastModificationTime', 'CreationTime')) {\n try { $stamp = $it.$property; if ($stamp -ne $null) { break } } catch {}\n }\n if ($olderThanDays -gt 0 -and $stamp -ne $null -and $stamp -gt $cutoff) { $kept++; continue }\n $matched++\n if ($dryRun) { continue }\n try { $it.Delete(); $purged++ } catch { $failed++ }\n}\nConvertTo-Json -Compress -InputObject ([PSCustomObject]@{\n folderPath = [string]$folder.FolderPath\n matched = $matched\n purged = $purged\n kept = $kept\n failed = $failed\n})\n`, 'purge');\n const e = record(output);\n return {\n folderPath: str(e.folderPath),\n dryRun: request.dryRun,\n matched: num(e.matched),\n purged: num(e.purged),\n kept: num(e.kept),\n failed: num(e.failed),\n };\n}\n"]}
@@ -1,40 +1,21 @@
1
- import type { DeleteDraftsResult, ListDraftsResult, SendAllDraftsResult, SendDraftsResult } from '../types';
1
+ import type { AccountRequest, EntryIdsRequest, ListDraftsRequest } from '../backend';
2
+ import type { DeleteDraftsResult, ListDraftsResult, SendDraftsResult } from '../types';
3
+ /** The account's drafts, newest first. Bodies are previewed, never returned whole. */
4
+ export declare function listOutlookDrafts(request: ListDraftsRequest): Promise<ListDraftsResult>;
5
+ /** Send the named drafts. */
6
+ export declare function sendDrafts(request: EntryIdsRequest): Promise<SendDraftsResult>;
2
7
  /**
3
- * Send the mail drafts that belong to `emailAccount`, wherever Outlook filed them.
4
- * Which drafts those are is DRAFTS_SCAN_PS's rule, not this function's.
8
+ * Send every draft belonging to the account.
5
9
  *
6
- * Item references are snapshotted before any Send() call: sending moves an item out
7
- * of Drafts, and mutating the collection mid-enumeration would skip every other one.
10
+ * The matching items are snapshotted before the first send: sending moves an
11
+ * item out of Drafts, and sending during a live enumeration would skip every
12
+ * other one.
8
13
  */
9
- export declare function sendAllDrafts(emailAccount: string): Promise<SendAllDraftsResult>;
14
+ export declare function sendAllDrafts(request: AccountRequest): Promise<SendDraftsResult>;
10
15
  /**
11
- * List the mail drafts belonging to `emailAccount`, newest first. Which drafts
12
- * those are is DRAFTS_SCAN_PS's rule.
13
- *
14
- * Deliberately returns no StoreID: deleteOutlookDrafts re-resolves each EntryID
15
- * against the same folders, so repeating a ~700-char store id on every row would be
16
- * pure payload. Bodies are previewed, never returned whole — a templated reply body
17
- * runs to tens of thousands of characters and a folder's worth would blow the cap.
18
- */
19
- export declare function listOutlookDrafts(emailAccount: string, limit?: number, previewChars?: number): Promise<ListDraftsResult>;
20
- /**
21
- * Delete mail drafts by EntryID. Outlook's Delete() moves the item to Deleted Items
22
- * rather than destroying it, so a mistaken call stays recoverable from there.
23
- *
24
- * Every id must resolve to a mail item sitting in one of THIS account's Drafts
25
- * folders before anything is deleted; an id pointing at ordinary mail, or at another
26
- * account's draft, is refused and reported. Without that gate this would be a
27
- * general-purpose "delete any email by id" tool, which is not what it is for.
28
- */
29
- export declare function deleteOutlookDrafts(emailAccount: string, entryIds: string[]): Promise<DeleteDraftsResult>;
30
- /**
31
- * Send a chosen subset of `emailAccount`'s drafts by EntryID — e.g. after
32
- * `listOutlookDrafts` and a user review pass — instead of `sendAllDrafts`'s
33
- * account-wide sweep.
34
- *
35
- * Same ownership gate as `deleteOutlookDrafts`: every id must resolve to a mail
36
- * item sitting in one of THIS account's Drafts folders before anything is sent.
37
- * An id pointing at ordinary inbox mail, or at another account's draft, is
38
- * refused and reported rather than sent.
16
+ * Delete the named drafts. Outlook's Delete() moves an item to Deleted Items,
17
+ * so a mistake stays recoverable. Without the proof that each is this
18
+ * account's draft, this would be a general "delete any email by id" call,
19
+ * which is not what it is for.
39
20
  */
40
- export declare function sendDrafts(emailAccount: string, entryIds: string[]): Promise<SendDraftsResult>;
21
+ export declare function deleteOutlookDrafts(request: EntryIdsRequest): Promise<DeleteDraftsResult>;