@dawnlit/outlook-bridge 1.1.1 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (139) hide show
  1. package/README.md +150 -183
  2. package/dist/backend.d.ts +151 -0
  3. package/dist/backend.js +3 -0
  4. package/dist/backend.js.map +1 -0
  5. package/dist/bridge.d.ts +56 -0
  6. package/dist/bridge.js +507 -0
  7. package/dist/bridge.js.map +1 -0
  8. package/dist/errors.d.ts +81 -43
  9. package/dist/errors.js +98 -52
  10. package/dist/errors.js.map +1 -1
  11. package/dist/index.d.ts +10 -10
  12. package/dist/index.js +25 -52
  13. package/dist/index.js.map +1 -1
  14. package/dist/mac/accounts.d.ts +7 -2
  15. package/dist/mac/accounts.js +23 -6
  16. package/dist/mac/accounts.js.map +1 -1
  17. package/dist/mac/attachments.d.ts +5 -25
  18. package/dist/mac/attachments.js +40 -99
  19. package/dist/mac/attachments.js.map +1 -1
  20. package/dist/mac/bounces.d.ts +12 -27
  21. package/dist/mac/bounces.js +95 -112
  22. package/dist/mac/bounces.js.map +1 -1
  23. package/dist/mac/cleanup.d.ts +11 -25
  24. package/dist/mac/cleanup.js +50 -76
  25. package/dist/mac/cleanup.js.map +1 -1
  26. package/dist/mac/drafts.d.ts +13 -34
  27. package/dist/mac/drafts.js +95 -169
  28. package/dist/mac/drafts.js.map +1 -1
  29. package/dist/mac/folders.d.ts +10 -21
  30. package/dist/mac/folders.js +33 -47
  31. package/dist/mac/folders.js.map +1 -1
  32. package/dist/mac/index.d.ts +2 -22
  33. package/dist/mac/index.js +19 -81
  34. package/dist/mac/index.js.map +1 -1
  35. package/dist/mac/profile.d.ts +13 -0
  36. package/dist/mac/profile.js +149 -0
  37. package/dist/mac/profile.js.map +1 -0
  38. package/dist/mac/read.d.ts +18 -38
  39. package/dist/mac/read.js +154 -210
  40. package/dist/mac/read.js.map +1 -1
  41. package/dist/mac/run.d.ts +35 -39
  42. package/dist/mac/run.js +97 -102
  43. package/dist/mac/run.js.map +1 -1
  44. package/dist/mac/scripts.d.ts +78 -86
  45. package/dist/mac/scripts.js +125 -115
  46. package/dist/mac/scripts.js.map +1 -1
  47. package/dist/mac/send.d.ts +10 -20
  48. package/dist/mac/send.js +64 -86
  49. package/dist/mac/send.js.map +1 -1
  50. package/dist/mac/signatures.d.ts +2 -2
  51. package/dist/mac/signatures.js +9 -11
  52. package/dist/mac/signatures.js.map +1 -1
  53. package/dist/mac/templates.d.ts +14 -26
  54. package/dist/mac/templates.js +75 -113
  55. package/dist/mac/templates.js.map +1 -1
  56. package/dist/mail.d.ts +77 -45
  57. package/dist/mail.js +166 -73
  58. package/dist/mail.js.map +1 -1
  59. package/dist/runtime.d.ts +76 -43
  60. package/dist/runtime.js +103 -48
  61. package/dist/runtime.js.map +1 -1
  62. package/dist/shared/args.d.ts +48 -0
  63. package/dist/shared/args.js +216 -0
  64. package/dist/shared/args.js.map +1 -0
  65. package/dist/shared/attachmentMatch.d.ts +28 -16
  66. package/dist/shared/attachmentMatch.js +60 -22
  67. package/dist/shared/attachmentMatch.js.map +1 -1
  68. package/dist/shared/bounceReport.d.ts +11 -0
  69. package/dist/shared/bounceReport.js +45 -0
  70. package/dist/shared/bounceReport.js.map +1 -0
  71. package/dist/shared/bounceRules.d.ts +38 -10
  72. package/dist/shared/bounceRules.js +50 -32
  73. package/dist/shared/bounceRules.js.map +1 -1
  74. package/dist/shared/exec.d.ts +21 -0
  75. package/dist/shared/exec.js +47 -0
  76. package/dist/shared/exec.js.map +1 -0
  77. package/dist/shared/json.d.ts +12 -6
  78. package/dist/shared/json.js +29 -23
  79. package/dist/shared/json.js.map +1 -1
  80. package/dist/shared/locate.d.ts +39 -0
  81. package/dist/shared/locate.js +129 -0
  82. package/dist/shared/locate.js.map +1 -0
  83. package/dist/shared/replyBody.d.ts +42 -32
  84. package/dist/shared/replyBody.js +84 -71
  85. package/dist/shared/replyBody.js.map +1 -1
  86. package/dist/templateBody.d.ts +63 -0
  87. package/dist/{outlookTemplateSections.js → templateBody.js} +72 -72
  88. package/dist/templateBody.js.map +1 -0
  89. package/dist/types.d.ts +360 -167
  90. package/dist/types.js +8 -9
  91. package/dist/types.js.map +1 -1
  92. package/dist/windows/accounts.d.ts +7 -12
  93. package/dist/windows/accounts.js +19 -34
  94. package/dist/windows/accounts.js.map +1 -1
  95. package/dist/windows/attachments.d.ts +9 -24
  96. package/dist/windows/attachments.js +94 -119
  97. package/dist/windows/attachments.js.map +1 -1
  98. package/dist/windows/bounces.d.ts +13 -26
  99. package/dist/windows/bounces.js +168 -200
  100. package/dist/windows/bounces.js.map +1 -1
  101. package/dist/windows/cleanup.d.ts +19 -23
  102. package/dist/windows/cleanup.js +92 -111
  103. package/dist/windows/cleanup.js.map +1 -1
  104. package/dist/windows/drafts.d.ts +16 -35
  105. package/dist/windows/drafts.js +167 -253
  106. package/dist/windows/drafts.js.map +1 -1
  107. package/dist/windows/folders.d.ts +8 -13
  108. package/dist/windows/folders.js +44 -52
  109. package/dist/windows/folders.js.map +1 -1
  110. package/dist/windows/index.d.ts +2 -27
  111. package/dist/windows/index.js +11 -66
  112. package/dist/windows/index.js.map +1 -1
  113. package/dist/windows/read.d.ts +24 -60
  114. package/dist/windows/read.js +261 -306
  115. package/dist/windows/read.js.map +1 -1
  116. package/dist/windows/run.d.ts +39 -39
  117. package/dist/windows/run.js +151 -111
  118. package/dist/windows/run.js.map +1 -1
  119. package/dist/windows/scripts.d.ts +65 -55
  120. package/dist/windows/scripts.js +109 -85
  121. package/dist/windows/scripts.js.map +1 -1
  122. package/dist/windows/send.d.ts +6 -11
  123. package/dist/windows/send.js +68 -88
  124. package/dist/windows/send.js.map +1 -1
  125. package/dist/windows/signatures.d.ts +4 -10
  126. package/dist/windows/signatures.js +30 -37
  127. package/dist/windows/signatures.js.map +1 -1
  128. package/dist/windows/templates.d.ts +12 -18
  129. package/dist/windows/templates.js +122 -149
  130. package/dist/windows/templates.js.map +1 -1
  131. package/package.json +4 -3
  132. package/dist/OutlookService.d.ts +0 -43
  133. package/dist/OutlookService.js +0 -137
  134. package/dist/OutlookService.js.map +0 -1
  135. package/dist/outlookTemplateSections.d.ts +0 -62
  136. package/dist/outlookTemplateSections.js.map +0 -1
  137. package/dist/shared/capabilities.d.ts +0 -3
  138. package/dist/shared/capabilities.js +0 -9
  139. package/dist/shared/capabilities.js.map +0 -1
@@ -1,79 +1,89 @@
1
- import type { MailFolderRef } from '../types';
1
+ import type { EmailLocator, MailFolderRef } from '../types';
2
2
  /**
3
- * Session plus the mailbox the address names; binds `$target`, `$account` and
4
- * `$storeFolder`.
3
+ * Open the MAPI session, binding `$outlook` and `$ns`.
5
4
  *
6
- * An address can name a mailbox two different ways, and only one of them is an
7
- * Account. A mailbox opened as a secondary store has no Account object, so
8
- * resolving through Accounts alone closes every operation to it — including the
9
- * reads, which never needed an Account in the first place. Both are resolved
10
- * here and EITHER is enough: `$account` is null for a store-only mailbox, and
11
- * the operations that genuinely require an identity to act as (sending) check
12
- * for it themselves rather than being denied the mailbox up front.
5
+ * `Logon()` is a no-op against an Outlook that is already running and starts
6
+ * one that isn't, which removes a class of "works only while Outlook is open"
7
+ * failure.
8
+ */
9
+ export declare const SESSION_PS = "\n$outlook = New-Object -ComObject Outlook.Application\n$ns = $outlook.GetNamespace('MAPI')\n$ns.Logon()\n";
10
+ /**
11
+ * The session plus the mailbox an address names; binds `$target`, `$account`
12
+ * and `$storeFolder`.
13
13
  *
14
- * Fails rather than falling back to the default account. A bad address must
15
- * never silently send from — or read — the wrong mailbox, and the thrown
16
- * sentence is the one `classifyRunFailure` recognises to raise
17
- * `AccountNotFoundError`, so the wording is load-bearing.
14
+ * An address can name a mailbox two ways, and only one is an Account. A mailbox
15
+ * mounted as a secondary store — a shared mailbox, or one added after Outlook
16
+ * started — has no Account object, so both are resolved and EITHER is enough:
17
+ * `$account` is null for a store-only mailbox, and the operations that need an
18
+ * identity to act as (sending) check for it themselves.
18
19
  *
19
- * The store is matched on the target address BEFORE the account's display name,
20
- * which is what keeps the named-store route (see `namedStoreScript`) selecting
21
- * the folders it selected before on a profile where the two differ.
20
+ * Never falls back to the default account: a bad address must not silently
21
+ * read or send from the wrong mailbox.
22
+ *
23
+ * The store is matched on the address BEFORE the account's display name, so a
24
+ * profile where the two differ selects the same folders it always has.
22
25
  */
23
26
  export declare function accountScript(emailAccount: string): string;
24
27
  /**
25
- * The mailbox's own store, as `$store`. Requires `accountScript`.
28
+ * The mailbox's store as `$store` (and its id as `$storeId`), reached through
29
+ * the account's delivery store. Requires `accountScript`.
26
30
  *
27
- * Falls back to the resolved store folder because a store-only mailbox has no
28
- * account to read a DeliveryStore off — reaching through `$account` directly is
29
- * what makes an operation fail on exactly those mailboxes.
31
+ * Falls back to the resolved store folder, because a store-only mailbox has no
32
+ * account to read a delivery store off.
30
33
  */
31
34
  export declare const DELIVERY_STORE_PS = "\n$store = if ($account -ne $null) { $account.DeliveryStore } else { $storeFolder.Store }\n$storeId = $store.StoreID\n";
32
35
  /**
33
- * The store `accountScript` resolved through the namespace's top-level folders,
34
- * as `$store`.
36
+ * The mailbox's store as `$store`, reached through the namespace's top-level
37
+ * folder for it. Requires `accountScript`.
35
38
  *
36
- * Kept alongside `DELIVERY_STORE_PS` rather than replaced by it: the reading
37
- * operations resolve the store this way, and on a profile where a mailbox is
38
- * open under a display name that differs from the delivery store's, the two do
39
- * not select the same folders. Changing which one a reader uses changes what it
40
- * returns, so each keeps the route it was verified against.
39
+ * Kept beside DELIVERY_STORE_PS rather than replaced by it: the readers resolve
40
+ * their store this way, and on a profile where a mailbox is open under a display
41
+ * name that differs from its delivery store's, the two routes do not select the
42
+ * same folders. Each operation keeps the route it was verified against.
43
+ */
44
+ export declare const NAMED_STORE_PS: string;
45
+ /**
46
+ * Resolve one email into `$<variable>`, or fail as NOT_FOUND.
41
47
  *
42
- * The folder search itself now lives in `accountScript`, which has to attempt it
43
- * anyway to resolve a mailbox that has no account. This throws when it found
44
- * nothing, which is the case where the address named an account whose store is
45
- * not open in this profile.
48
+ * The StoreID is tried first, since it lets an id from any mounted mailbox
49
+ * resolve; the bare lookup (default store) is the fallback for a caller holding
50
+ * a stale StoreID or none.
46
51
  */
47
- export declare function namedStoreScript(emailAccount: string): string;
52
+ export declare function itemLookupScript(email: EmailLocator, variable?: string): string;
48
53
  /**
49
- * Resolve one item into `$item`. A StoreID disambiguates across mailboxes, so
50
- * pass it whenever the listing that produced the id carried one.
54
+ * The sender's SMTP address as `$senderSmtp`. Requires `$item`.
55
+ *
56
+ * An Exchange sender's SenderEmailAddress is an X.500 DN, not an address; a
57
+ * caller replying to it or matching it against anything needs the address.
51
58
  */
52
- export declare function getItemScript(entryId: string, storeId?: string): string;
59
+ export declare const SENDER_SMTP_PS = "\n$senderSmtp = ''\ntry {\n if ($item.SenderEmailType -eq 'EX') { $senderSmtp = [string]$item.Sender.GetExchangeUser().PrimarySmtpAddress }\n} catch {}\nif (-not $senderSmtp) { try { $senderSmtp = [string]$item.SenderEmailAddress } catch {} }\n";
60
+ /** Recursive, case-insensitive folder search by name, depth-capped so a huge tree can't hang it. */
61
+ export declare const FIND_FOLDER_PS = "\nfunction Find-FolderByName($root, [string]$name, [int]$depth) {\n foreach ($f in $root.Folders) {\n if ($f.Name -ieq $name) { return $f }\n }\n if ($depth -le 1) { return $null }\n foreach ($f in $root.Folders) {\n $hit = Find-FolderByName $f $name ($depth - 1)\n if ($hit -ne $null) { return $hit }\n }\n return $null\n}\n";
53
62
  /**
54
- * The sender's real SMTP address, as `$senderSmtp`. Requires `$item`.
63
+ * `$cutoff`, the moment `daysBack` days ago, and `$cutoffFilter`, that moment
64
+ * as a date string for an `Items.Restrict` filter.
55
65
  *
56
- * An Exchange sender's `SenderEmailAddress` is an X500 DN rather than an
57
- * address; a caller that means to reply to it, or correlate it with anything,
58
- * needs the address it resolves to.
66
+ * Restrict parses date strings in the machine's own regional format, so the
67
+ * string is formatted in it ('g': short date and time) rather than a fixed
68
+ * pattern — 'MM/dd/yyyy' reads as a different day on a day-first locale. Every
69
+ * reader re-checks each item against `$cutoff` too, so the window holds even
70
+ * where a store reads the filter loosely.
59
71
  */
60
- export declare const SENDER_SMTP_PS = "\n$senderSmtp = ''\ntry {\n if ($item.SenderEmailType -eq 'EX') { $senderSmtp = $item.Sender.GetExchangeUser().PrimarySmtpAddress }\n} catch {}\nif (-not $senderSmtp) { $senderSmtp = if ($item.SenderEmailAddress) { $item.SenderEmailAddress } else { '' } }\n";
72
+ export declare function cutoffScript(daysBack: number): string;
61
73
  /**
62
- * Recursive folder-by-name search. Case-insensitive, depth-capped so a huge
63
- * mailbox tree can't hang the scan.
74
+ * `$<variable>` = the items of `$<folderVariable>` with `property` on or after
75
+ * `$cutoff`. Requires `cutoffScript`. A store that rejects the filter outright
76
+ * yields every item instead, which the per-item re-check then narrows.
64
77
  */
65
- export declare const FIND_FOLDER_PS = "\nfunction Find-FolderByName($root, $name, $depth) {\n foreach ($f in $root.Folders) {\n if ($f.Name -ieq $name) { return $f }\n }\n if ($depth -le 1) { return $null }\n foreach ($f in $root.Folders) {\n $hit = Find-FolderByName $f $name ($depth - 1)\n if ($hit -ne $null) { return $hit }\n }\n return $null\n}\n";
78
+ export declare function itemsSinceScript(folderVariable: string, property: string, variable: string): string;
66
79
  /**
67
- * Emit the PowerShell that resolves `$scope` from `$store`, walking a well-known
68
- * root down through any further segments. Also binds `$scopeCreated`.
69
- *
70
- * Split out so the emitted script can be parse-checked without an Outlook
71
- * session — the folder walk is the only part that varies per call, and a syntax
72
- * error in it would only ever surface as a failed live run.
80
+ * Resolve `$scope` from `$store`: a well-known root walked down through any
81
+ * further segments. Also binds `$scopeCreated`.
73
82
  *
74
- * `folderLabel` is the caller's original string, used verbatim in the error so
75
- * the message names what they typed rather than the parsed segments. Set
76
- * `createMissing` to build absent segments instead of throwing — the whole
77
- * chain, so a nested destination can be created in one call.
83
+ * Segments match direct children; a bare name that isn't a direct child falls
84
+ * back to a recursive search, so one folder string works across operations. A
85
+ * missing folder fails as NOT_FOUND naming what the caller typed and what the
86
+ * deepest folder reached does hold — never a silent fall back to the root. With
87
+ * `createMissing`, the whole missing chain is created instead.
78
88
  */
79
- export declare function mailScopeScript(ref: MailFolderRef, folderLabel?: string, createMissing?: boolean): string;
89
+ export declare function mailScopeScript(ref: MailFolderRef, folderLabel: string, createMissing?: boolean): string;
@@ -1,53 +1,50 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.FIND_FOLDER_PS = exports.SENDER_SMTP_PS = exports.DELIVERY_STORE_PS = void 0;
3
+ exports.FIND_FOLDER_PS = exports.SENDER_SMTP_PS = exports.NAMED_STORE_PS = exports.DELIVERY_STORE_PS = exports.SESSION_PS = void 0;
4
4
  exports.accountScript = accountScript;
5
- exports.namedStoreScript = namedStoreScript;
6
- exports.getItemScript = getItemScript;
5
+ exports.itemLookupScript = itemLookupScript;
6
+ exports.cutoffScript = cutoffScript;
7
+ exports.itemsSinceScript = itemsSinceScript;
7
8
  exports.mailScopeScript = mailScopeScript;
8
9
  // The PowerShell fragments more than one Windows operation is built from.
9
10
  //
10
- // A fragment lives here once at least two modules emit it; one used by a single
11
- // operation stays beside that operation, where its rules are readable next to
12
- // the code that depends on them.
11
+ // A fragment lives here once two modules emit it; one used by a single
12
+ // operation stays beside that operation, where its rules read next to the code
13
+ // relying on them. Every fragment follows run.ts's escaping rule: caller text
14
+ // only ever appears as a psString literal.
13
15
  const run_1 = require("./run");
16
+ const errors_1 = require("../errors");
14
17
  /**
15
- * Open a MAPI session. `$outlook` and `$ns` are what every later fragment reads.
18
+ * Open the MAPI session, binding `$outlook` and `$ns`.
16
19
  *
17
- * `Logon()` is a no-op against an Outlook that is already running and starts one
18
- * that isn't, so it costs nothing and removes a class of "works only when
19
- * Outlook happens to be open" failure.
20
+ * `Logon()` is a no-op against an Outlook that is already running and starts
21
+ * one that isn't, which removes a class of "works only while Outlook is open"
22
+ * failure.
20
23
  */
21
- const PS_PRELUDE = `
22
- $ErrorActionPreference = 'Stop'
24
+ exports.SESSION_PS = `
23
25
  $outlook = New-Object -ComObject Outlook.Application
24
- $ns = $outlook.GetNamespace('mapi')
26
+ $ns = $outlook.GetNamespace('MAPI')
25
27
  $ns.Logon()
26
28
  `;
27
29
  /**
28
- * Session plus the mailbox the address names; binds `$target`, `$account` and
29
- * `$storeFolder`.
30
+ * The session plus the mailbox an address names; binds `$target`, `$account`
31
+ * and `$storeFolder`.
30
32
  *
31
- * An address can name a mailbox two different ways, and only one of them is an
32
- * Account. A mailbox opened as a secondary store has no Account object, so
33
- * resolving through Accounts alone closes every operation to it — including the
34
- * reads, which never needed an Account in the first place. Both are resolved
35
- * here and EITHER is enough: `$account` is null for a store-only mailbox, and
36
- * the operations that genuinely require an identity to act as (sending) check
37
- * for it themselves rather than being denied the mailbox up front.
33
+ * An address can name a mailbox two ways, and only one is an Account. A mailbox
34
+ * mounted as a secondary store — a shared mailbox, or one added after Outlook
35
+ * started — has no Account object, so both are resolved and EITHER is enough:
36
+ * `$account` is null for a store-only mailbox, and the operations that need an
37
+ * identity to act as (sending) check for it themselves.
38
38
  *
39
- * Fails rather than falling back to the default account. A bad address must
40
- * never silently send from — or read — the wrong mailbox, and the thrown
41
- * sentence is the one `classifyRunFailure` recognises to raise
42
- * `AccountNotFoundError`, so the wording is load-bearing.
39
+ * Never falls back to the default account: a bad address must not silently
40
+ * read or send from the wrong mailbox.
43
41
  *
44
- * The store is matched on the target address BEFORE the account's display name,
45
- * which is what keeps the named-store route (see `namedStoreScript`) selecting
46
- * the folders it selected before on a profile where the two differ.
42
+ * The store is matched on the address BEFORE the account's display name, so a
43
+ * profile where the two differ selects the same folders it always has.
47
44
  */
48
45
  function accountScript(emailAccount) {
49
- return `${PS_PRELUDE}
50
- $target = '${(0, run_1.psEscape)(emailAccount)}'
46
+ return `${exports.SESSION_PS}
47
+ $target = ${(0, run_1.psString)(emailAccount)}
51
48
  $account = $null
52
49
  foreach ($a in $ns.Accounts) {
53
50
  if ($a.SmtpAddress -ieq $target) { $account = $a; break }
@@ -61,71 +58,75 @@ if ($storeFolder -eq $null -and $account -ne $null) {
61
58
  if ($f.Name -ieq $account.DisplayName) { $storeFolder = $f; break }
62
59
  }
63
60
  }
64
- if ($account -eq $null -and $storeFolder -eq $null) { throw "Account '$target' not found" }
61
+ if ($account -eq $null -and $storeFolder -eq $null) {
62
+ throw "${(0, errors_1.failureTag)('ACCOUNT_NOT_FOUND')}Account '$target' not found in this Outlook profile."
63
+ }
65
64
  `;
66
65
  }
67
66
  /**
68
- * The mailbox's own store, as `$store`. Requires `accountScript`.
67
+ * The mailbox's store as `$store` (and its id as `$storeId`), reached through
68
+ * the account's delivery store. Requires `accountScript`.
69
69
  *
70
- * Falls back to the resolved store folder because a store-only mailbox has no
71
- * account to read a DeliveryStore off — reaching through `$account` directly is
72
- * what makes an operation fail on exactly those mailboxes.
70
+ * Falls back to the resolved store folder, because a store-only mailbox has no
71
+ * account to read a delivery store off.
73
72
  */
74
73
  exports.DELIVERY_STORE_PS = `
75
74
  $store = if ($account -ne $null) { $account.DeliveryStore } else { $storeFolder.Store }
76
75
  $storeId = $store.StoreID
77
76
  `;
78
77
  /**
79
- * The store `accountScript` resolved through the namespace's top-level folders,
80
- * as `$store`.
81
- *
82
- * Kept alongside `DELIVERY_STORE_PS` rather than replaced by it: the reading
83
- * operations resolve the store this way, and on a profile where a mailbox is
84
- * open under a display name that differs from the delivery store's, the two do
85
- * not select the same folders. Changing which one a reader uses changes what it
86
- * returns, so each keeps the route it was verified against.
78
+ * The mailbox's store as `$store`, reached through the namespace's top-level
79
+ * folder for it. Requires `accountScript`.
87
80
  *
88
- * The folder search itself now lives in `accountScript`, which has to attempt it
89
- * anyway to resolve a mailbox that has no account. This throws when it found
90
- * nothing, which is the case where the address named an account whose store is
91
- * not open in this profile.
81
+ * Kept beside DELIVERY_STORE_PS rather than replaced by it: the readers resolve
82
+ * their store this way, and on a profile where a mailbox is open under a display
83
+ * name that differs from its delivery store's, the two routes do not select the
84
+ * same folders. Each operation keeps the route it was verified against.
92
85
  */
93
- function namedStoreScript(emailAccount) {
94
- return `
95
- if ($storeFolder -eq $null) { throw "Store folder not found for account '${(0, run_1.psEscape)(emailAccount)}'" }
86
+ exports.NAMED_STORE_PS = `
87
+ if ($storeFolder -eq $null) {
88
+ throw "${(0, errors_1.failureTag)('ACCOUNT_NOT_FOUND')}Account '$target' has no mailbox open in this Outlook profile."
89
+ }
96
90
  $store = $storeFolder.Store
97
91
  $storeId = $store.StoreID
98
92
  `;
99
- }
100
93
  /**
101
- * Resolve one item into `$item`. A StoreID disambiguates across mailboxes, so
102
- * pass it whenever the listing that produced the id carried one.
94
+ * Resolve one email into `$<variable>`, or fail as NOT_FOUND.
95
+ *
96
+ * The StoreID is tried first, since it lets an id from any mounted mailbox
97
+ * resolve; the bare lookup (default store) is the fallback for a caller holding
98
+ * a stale StoreID or none.
103
99
  */
104
- function getItemScript(entryId, storeId) {
105
- return storeId
106
- ? `$item = $ns.GetItemFromID('${(0, run_1.psEscape)(entryId)}', '${(0, run_1.psEscape)(storeId)}')`
107
- : `$item = $ns.GetItemFromID('${(0, run_1.psEscape)(entryId)}')`;
100
+ function itemLookupScript(email, variable = 'item') {
101
+ const byStore = email.storeId
102
+ ? `try { $${variable} = $ns.GetItemFromID($lookupId, ${(0, run_1.psString)(email.storeId)}) } catch { $${variable} = $null }`
103
+ : '';
104
+ return `
105
+ $lookupId = ${(0, run_1.psString)(email.entryId)}
106
+ $${variable} = $null
107
+ ${byStore}
108
+ if ($${variable} -eq $null) { try { $${variable} = $ns.GetItemFromID($lookupId) } catch { $${variable} = $null } }
109
+ if ($${variable} -eq $null) {
110
+ throw "${(0, errors_1.failureTag)('NOT_FOUND', 'email')}No email found for entry id '$lookupId'. It may have been moved or deleted - list the mail again for a current id."
111
+ }
112
+ `;
108
113
  }
109
114
  /**
110
- * The sender's real SMTP address, as `$senderSmtp`. Requires `$item`.
115
+ * The sender's SMTP address as `$senderSmtp`. Requires `$item`.
111
116
  *
112
- * An Exchange sender's `SenderEmailAddress` is an X500 DN rather than an
113
- * address; a caller that means to reply to it, or correlate it with anything,
114
- * needs the address it resolves to.
117
+ * An Exchange sender's SenderEmailAddress is an X.500 DN, not an address; a
118
+ * caller replying to it or matching it against anything needs the address.
115
119
  */
116
120
  exports.SENDER_SMTP_PS = `
117
121
  $senderSmtp = ''
118
122
  try {
119
- if ($item.SenderEmailType -eq 'EX') { $senderSmtp = $item.Sender.GetExchangeUser().PrimarySmtpAddress }
123
+ if ($item.SenderEmailType -eq 'EX') { $senderSmtp = [string]$item.Sender.GetExchangeUser().PrimarySmtpAddress }
120
124
  } catch {}
121
- if (-not $senderSmtp) { $senderSmtp = if ($item.SenderEmailAddress) { $item.SenderEmailAddress } else { '' } }
125
+ if (-not $senderSmtp) { try { $senderSmtp = [string]$item.SenderEmailAddress } catch {} }
122
126
  `;
123
- /**
124
- * Recursive folder-by-name search. Case-insensitive, depth-capped so a huge
125
- * mailbox tree can't hang the scan.
126
- */
127
+ /** Recursive, case-insensitive folder search by name, depth-capped so a huge tree can't hang it. */
127
128
  exports.FIND_FOLDER_PS = `
128
- function Find-FolderByName($root, $name, $depth) {
129
+ function Find-FolderByName($root, [string]$name, [int]$depth) {
129
130
  foreach ($f in $root.Folders) {
130
131
  if ($f.Name -ieq $name) { return $f }
131
132
  }
@@ -138,25 +139,50 @@ function Find-FolderByName($root, $name, $depth) {
138
139
  }
139
140
  `;
140
141
  /**
141
- * Emit the PowerShell that resolves `$scope` from `$store`, walking a well-known
142
- * root down through any further segments. Also binds `$scopeCreated`.
142
+ * `$cutoff`, the moment `daysBack` days ago, and `$cutoffFilter`, that moment
143
+ * as a date string for an `Items.Restrict` filter.
143
144
  *
144
- * Split out so the emitted script can be parse-checked without an Outlook
145
- * session — the folder walk is the only part that varies per call, and a syntax
146
- * error in it would only ever surface as a failed live run.
145
+ * Restrict parses date strings in the machine's own regional format, so the
146
+ * string is formatted in it ('g': short date and time) rather than a fixed
147
+ * pattern — 'MM/dd/yyyy' reads as a different day on a day-first locale. Every
148
+ * reader re-checks each item against `$cutoff` too, so the window holds even
149
+ * where a store reads the filter loosely.
150
+ */
151
+ function cutoffScript(daysBack) {
152
+ return `
153
+ $cutoff = (Get-Date).AddDays(-${(0, run_1.psInt)(daysBack)})
154
+ $cutoffFilter = $cutoff.ToString('g').Replace("'", "''")
155
+ `;
156
+ }
157
+ /**
158
+ * `$<variable>` = the items of `$<folderVariable>` with `property` on or after
159
+ * `$cutoff`. Requires `cutoffScript`. A store that rejects the filter outright
160
+ * yields every item instead, which the per-item re-check then narrows.
161
+ */
162
+ function itemsSinceScript(folderVariable, property, variable) {
163
+ return `
164
+ $${variable} = $null
165
+ try { $${variable} = $${folderVariable}.Items.Restrict("[${property}] >= '$cutoffFilter'") } catch { $${variable} = $${folderVariable}.Items }
166
+ `;
167
+ }
168
+ /**
169
+ * Resolve `$scope` from `$store`: a well-known root walked down through any
170
+ * further segments. Also binds `$scopeCreated`.
147
171
  *
148
- * `folderLabel` is the caller's original string, used verbatim in the error so
149
- * the message names what they typed rather than the parsed segments. Set
150
- * `createMissing` to build absent segments instead of throwing — the whole
151
- * chain, so a nested destination can be created in one call.
172
+ * Segments match direct children; a bare name that isn't a direct child falls
173
+ * back to a recursive search, so one folder string works across operations. A
174
+ * missing folder fails as NOT_FOUND naming what the caller typed and what the
175
+ * deepest folder reached does hold — never a silent fall back to the root. With
176
+ * `createMissing`, the whole missing chain is created instead.
152
177
  */
153
- function mailScopeScript(ref, folderLabel = '', createMissing = false) {
154
- const root = `$store.GetDefaultFolder(${ref.rootId})`;
178
+ function mailScopeScript(ref, folderLabel, createMissing = false) {
179
+ const root = `$store.GetDefaultFolder(${(0, run_1.psInt)(ref.rootId)})`;
155
180
  if (ref.segments.length === 0)
156
181
  return `$scope = ${root}\n$scopeCreated = $false`;
157
182
  return `
158
183
  ${exports.FIND_FOLDER_PS}
159
- $segments = @(${ref.segments.map(s => `'${(0, run_1.psEscape)(s)}'`).join(',')})
184
+ $folderLabel = ${(0, run_1.psString)(folderLabel)}
185
+ $segments = @(${ref.segments.map(run_1.psString).join(', ')})
160
186
  $scopeRoot = ${root}
161
187
  $scope = $scopeRoot
162
188
  $deepest = $scopeRoot
@@ -167,13 +193,11 @@ foreach ($seg in $segments) {
167
193
  if ($next -eq $null) { $deepest = $scope; $scope = $null; break }
168
194
  $scope = $next
169
195
  }
170
- # A bare name that isn't a direct child still resolves by recursive search, so one
171
- # folder string keeps working across tools.
172
196
  if ($scope -eq $null -and $segments.Count -eq 1) {
173
197
  $scope = Find-FolderByName $scopeRoot $segments[0] 3
174
198
  }
175
199
  if ($scope -eq $null -and ${createMissing ? '$true' : '$false'}) {
176
- # Rebuild the whole chain from the root, creating only what is genuinely absent.
200
+ # Rebuild the chain from the root, creating only what is genuinely absent.
177
201
  $scope = $scopeRoot
178
202
  foreach ($seg in $segments) {
179
203
  $next = $null
@@ -185,7 +209,7 @@ if ($scope -eq $null -and ${createMissing ? '$true' : '$false'}) {
185
209
  if ($scope -eq $null) {
186
210
  $names = @()
187
211
  foreach ($f in $deepest.Folders) { $names += $f.Name }
188
- throw "Folder '${(0, run_1.psEscape)(folderLabel)}' not found. Folders under '$($deepest.Name)': $($names -join ', ')"
212
+ throw "${(0, errors_1.failureTag)('NOT_FOUND', 'folder')}Folder '$folderLabel' not found. Folders under '$($deepest.Name)': $($names -join ', ')"
189
213
  }`;
190
214
  }
191
215
  //# sourceMappingURL=scripts.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"scripts.js","sourceRoot":"","sources":["../../src/windows/scripts.ts"],"names":[],"mappings":";;;AA2CA,sCAkBC;AA6BD,4CAMC;AAMD,sCAIC;AAgDD,0CAoCC;AA9LD,0EAA0E;AAC1E,EAAE;AACF,gFAAgF;AAChF,8EAA8E;AAC9E,iCAAiC;AACjC,+BAAiC;AAGjC;;;;;;GAMG;AACH,MAAM,UAAU,GAAG;;;;;CAKlB,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,SAAgB,aAAa,CAAC,YAAoB;IAC9C,OAAO,GAAG,UAAU;aACX,IAAA,cAAQ,EAAC,YAAY,CAAC;;;;;;;;;;;;;;;CAelC,CAAC;AACF,CAAC;AAED;;;;;;GAMG;AACU,QAAA,iBAAiB,GAAG;;;CAGhC,CAAC;AAEF;;;;;;;;;;;;;;GAcG;AACH,SAAgB,gBAAgB,CAAC,YAAoB;IACjD,OAAO;2EACgE,IAAA,cAAQ,EAAC,YAAY,CAAC;;;CAGhG,CAAC;AACF,CAAC;AAED;;;GAGG;AACH,SAAgB,aAAa,CAAC,OAAe,EAAE,OAAgB;IAC3D,OAAO,OAAO;QACV,CAAC,CAAC,8BAA8B,IAAA,cAAQ,EAAC,OAAO,CAAC,OAAO,IAAA,cAAQ,EAAC,OAAO,CAAC,IAAI;QAC7E,CAAC,CAAC,8BAA8B,IAAA,cAAQ,EAAC,OAAO,CAAC,IAAI,CAAC;AAC9D,CAAC;AAED;;;;;;GAMG;AACU,QAAA,cAAc,GAAG;;;;;;CAM7B,CAAC;AAEF;;;GAGG;AACU,QAAA,cAAc,GAAG;;;;;;;;;;;;CAY7B,CAAC;AAEF;;;;;;;;;;;;GAYG;AACH,SAAgB,eAAe,CAAC,GAAkB,EAAE,WAAW,GAAG,EAAE,EAAE,aAAa,GAAG,KAAK;IACvF,MAAM,IAAI,GAAG,2BAA2B,GAAG,CAAC,MAAM,GAAG,CAAC;IACtD,IAAI,GAAG,CAAC,QAAQ,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,YAAY,IAAI,0BAA0B,CAAC;IACjF,OAAO;EACT,sBAAc;gBACA,GAAG,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,IAAI,IAAA,cAAQ,EAAC,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC;eACpD,IAAI;;;;;;;;;;;;;;;4BAeS,aAAa,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,QAAQ;;;;;;;;;;;;;qBAazC,IAAA,cAAQ,EAAC,WAAW,CAAC;EACxC,CAAC;AACH,CAAC"}
1
+ {"version":3,"file":"scripts.js","sourceRoot":"","sources":["../../src/windows/scripts.ts"],"names":[],"mappings":";;;AAuCA,sCAoBC;AAsCD,4CAaC;AAyCD,oCAKC;AAOD,4CAKC;AAYD,0CAmCC;AAvND,0EAA0E;AAC1E,EAAE;AACF,uEAAuE;AACvE,+EAA+E;AAC/E,8EAA8E;AAC9E,2CAA2C;AAC3C,+BAAwC;AACxC,sCAAuC;AAGvC;;;;;;GAMG;AACU,QAAA,UAAU,GAAG;;;;CAIzB,CAAC;AAEF;;;;;;;;;;;;;;;GAeG;AACH,SAAgB,aAAa,CAAC,YAAoB;IAC9C,OAAO,GAAG,kBAAU;YACZ,IAAA,cAAQ,EAAC,YAAY,CAAC;;;;;;;;;;;;;;;aAerB,IAAA,mBAAU,EAAC,mBAAmB,CAAC;;CAE3C,CAAC;AACF,CAAC;AAED;;;;;;GAMG;AACU,QAAA,iBAAiB,GAAG;;;CAGhC,CAAC;AAEF;;;;;;;;GAQG;AACU,QAAA,cAAc,GAAG;;aAEjB,IAAA,mBAAU,EAAC,mBAAmB,CAAC;;;;CAI3C,CAAC;AAEF;;;;;;GAMG;AACH,SAAgB,gBAAgB,CAAC,KAAmB,EAAE,QAAQ,GAAG,MAAM;IACnE,MAAM,OAAO,GAAG,KAAK,CAAC,OAAO;QACzB,CAAC,CAAC,UAAU,QAAQ,mCAAmC,IAAA,cAAQ,EAAC,KAAK,CAAC,OAAO,CAAC,gBAAgB,QAAQ,YAAY;QAClH,CAAC,CAAC,EAAE,CAAC;IACT,OAAO;cACG,IAAA,cAAQ,EAAC,KAAK,CAAC,OAAO,CAAC;GAClC,QAAQ;EACT,OAAO;OACF,QAAQ,wBAAwB,QAAQ,8CAA8C,QAAQ;OAC9F,QAAQ;aACF,IAAA,mBAAU,EAAC,WAAW,EAAE,OAAO,CAAC;;CAE5C,CAAC;AACF,CAAC;AAED;;;;;GAKG;AACU,QAAA,cAAc,GAAG;;;;;;CAM7B,CAAC;AAEF,oGAAoG;AACvF,QAAA,cAAc,GAAG;;;;;;;;;;;;CAY7B,CAAC;AAEF;;;;;;;;;GASG;AACH,SAAgB,YAAY,CAAC,QAAgB;IACzC,OAAO;gCACqB,IAAA,WAAK,EAAC,QAAQ,CAAC;;CAE9C,CAAC;AACF,CAAC;AAED;;;;GAIG;AACH,SAAgB,gBAAgB,CAAC,cAAsB,EAAE,QAAgB,EAAE,QAAgB;IACvF,OAAO;GACR,QAAQ;SACF,QAAQ,OAAO,cAAc,qBAAqB,QAAQ,qCAAqC,QAAQ,OAAO,cAAc;CACpI,CAAC;AACF,CAAC;AAED;;;;;;;;;GASG;AACH,SAAgB,eAAe,CAAC,GAAkB,EAAE,WAAmB,EAAE,aAAa,GAAG,KAAK;IAC1F,MAAM,IAAI,GAAG,2BAA2B,IAAA,WAAK,EAAC,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC;IAC7D,IAAI,GAAG,CAAC,QAAQ,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,YAAY,IAAI,0BAA0B,CAAC;IACjF,OAAO;EACT,sBAAc;iBACC,IAAA,cAAQ,EAAC,WAAW,CAAC;gBACtB,GAAG,CAAC,QAAQ,CAAC,GAAG,CAAC,cAAQ,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC;eACtC,IAAI;;;;;;;;;;;;;4BAaS,aAAa,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,QAAQ;;;;;;;;;;;;;aAajD,IAAA,mBAAU,EAAC,WAAW,EAAE,QAAQ,CAAC;EAC5C,CAAC;AACH,CAAC","sourcesContent":["// The PowerShell fragments more than one Windows operation is built from.\n//\n// A fragment lives here once two modules emit it; one used by a single\n// operation stays beside that operation, where its rules read next to the code\n// relying on them. Every fragment follows run.ts's escaping rule: caller text\n// only ever appears as a psString literal.\nimport { psInt, psString } from './run';\nimport { failureTag } from '../errors';\nimport type { EmailLocator, MailFolderRef } from '../types';\n\n/**\n * Open the MAPI session, binding `$outlook` and `$ns`.\n *\n * `Logon()` is a no-op against an Outlook that is already running and starts\n * one that isn't, which removes a class of \"works only while Outlook is open\"\n * failure.\n */\nexport const SESSION_PS = `\n$outlook = New-Object -ComObject Outlook.Application\n$ns = $outlook.GetNamespace('MAPI')\n$ns.Logon()\n`;\n\n/**\n * The session plus the mailbox an address names; binds `$target`, `$account`\n * and `$storeFolder`.\n *\n * An address can name a mailbox two ways, and only one is an Account. A mailbox\n * mounted as a secondary store — a shared mailbox, or one added after Outlook\n * started — has no Account object, so both are resolved and EITHER is enough:\n * `$account` is null for a store-only mailbox, and the operations that need an\n * identity to act as (sending) check for it themselves.\n *\n * Never falls back to the default account: a bad address must not silently\n * read or send from the wrong mailbox.\n *\n * The store is matched on the address BEFORE the account's display name, so a\n * profile where the two differ selects the same folders it always has.\n */\nexport function accountScript(emailAccount: string): string {\n return `${SESSION_PS}\n$target = ${psString(emailAccount)}\n$account = $null\nforeach ($a in $ns.Accounts) {\n if ($a.SmtpAddress -ieq $target) { $account = $a; break }\n}\n$storeFolder = $null\nforeach ($f in $ns.Folders) {\n if ($f.Name -ieq $target) { $storeFolder = $f; break }\n}\nif ($storeFolder -eq $null -and $account -ne $null) {\n foreach ($f in $ns.Folders) {\n if ($f.Name -ieq $account.DisplayName) { $storeFolder = $f; break }\n }\n}\nif ($account -eq $null -and $storeFolder -eq $null) {\n throw \"${failureTag('ACCOUNT_NOT_FOUND')}Account '$target' not found in this Outlook profile.\"\n}\n`;\n}\n\n/**\n * The mailbox's store as `$store` (and its id as `$storeId`), reached through\n * the account's delivery store. Requires `accountScript`.\n *\n * Falls back to the resolved store folder, because a store-only mailbox has no\n * account to read a delivery store off.\n */\nexport const DELIVERY_STORE_PS = `\n$store = if ($account -ne $null) { $account.DeliveryStore } else { $storeFolder.Store }\n$storeId = $store.StoreID\n`;\n\n/**\n * The mailbox's store as `$store`, reached through the namespace's top-level\n * folder for it. Requires `accountScript`.\n *\n * Kept beside DELIVERY_STORE_PS rather than replaced by it: the readers resolve\n * their store this way, and on a profile where a mailbox is open under a display\n * name that differs from its delivery store's, the two routes do not select the\n * same folders. Each operation keeps the route it was verified against.\n */\nexport const NAMED_STORE_PS = `\nif ($storeFolder -eq $null) {\n throw \"${failureTag('ACCOUNT_NOT_FOUND')}Account '$target' has no mailbox open in this Outlook profile.\"\n}\n$store = $storeFolder.Store\n$storeId = $store.StoreID\n`;\n\n/**\n * Resolve one email into `$<variable>`, or fail as NOT_FOUND.\n *\n * The StoreID is tried first, since it lets an id from any mounted mailbox\n * resolve; the bare lookup (default store) is the fallback for a caller holding\n * a stale StoreID or none.\n */\nexport function itemLookupScript(email: EmailLocator, variable = 'item'): string {\n const byStore = email.storeId\n ? `try { $${variable} = $ns.GetItemFromID($lookupId, ${psString(email.storeId)}) } catch { $${variable} = $null }`\n : '';\n return `\n$lookupId = ${psString(email.entryId)}\n$${variable} = $null\n${byStore}\nif ($${variable} -eq $null) { try { $${variable} = $ns.GetItemFromID($lookupId) } catch { $${variable} = $null } }\nif ($${variable} -eq $null) {\n throw \"${failureTag('NOT_FOUND', 'email')}No email found for entry id '$lookupId'. It may have been moved or deleted - list the mail again for a current id.\"\n}\n`;\n}\n\n/**\n * The sender's SMTP address as `$senderSmtp`. Requires `$item`.\n *\n * An Exchange sender's SenderEmailAddress is an X.500 DN, not an address; a\n * caller replying to it or matching it against anything needs the address.\n */\nexport const SENDER_SMTP_PS = `\n$senderSmtp = ''\ntry {\n if ($item.SenderEmailType -eq 'EX') { $senderSmtp = [string]$item.Sender.GetExchangeUser().PrimarySmtpAddress }\n} catch {}\nif (-not $senderSmtp) { try { $senderSmtp = [string]$item.SenderEmailAddress } catch {} }\n`;\n\n/** Recursive, case-insensitive folder search by name, depth-capped so a huge tree can't hang it. */\nexport const FIND_FOLDER_PS = `\nfunction Find-FolderByName($root, [string]$name, [int]$depth) {\n foreach ($f in $root.Folders) {\n if ($f.Name -ieq $name) { return $f }\n }\n if ($depth -le 1) { return $null }\n foreach ($f in $root.Folders) {\n $hit = Find-FolderByName $f $name ($depth - 1)\n if ($hit -ne $null) { return $hit }\n }\n return $null\n}\n`;\n\n/**\n * `$cutoff`, the moment `daysBack` days ago, and `$cutoffFilter`, that moment\n * as a date string for an `Items.Restrict` filter.\n *\n * Restrict parses date strings in the machine's own regional format, so the\n * string is formatted in it ('g': short date and time) rather than a fixed\n * pattern — 'MM/dd/yyyy' reads as a different day on a day-first locale. Every\n * reader re-checks each item against `$cutoff` too, so the window holds even\n * where a store reads the filter loosely.\n */\nexport function cutoffScript(daysBack: number): string {\n return `\n$cutoff = (Get-Date).AddDays(-${psInt(daysBack)})\n$cutoffFilter = $cutoff.ToString('g').Replace(\"'\", \"''\")\n`;\n}\n\n/**\n * `$<variable>` = the items of `$<folderVariable>` with `property` on or after\n * `$cutoff`. Requires `cutoffScript`. A store that rejects the filter outright\n * yields every item instead, which the per-item re-check then narrows.\n */\nexport function itemsSinceScript(folderVariable: string, property: string, variable: string): string {\n return `\n$${variable} = $null\ntry { $${variable} = $${folderVariable}.Items.Restrict(\"[${property}] >= '$cutoffFilter'\") } catch { $${variable} = $${folderVariable}.Items }\n`;\n}\n\n/**\n * Resolve `$scope` from `$store`: a well-known root walked down through any\n * further segments. Also binds `$scopeCreated`.\n *\n * Segments match direct children; a bare name that isn't a direct child falls\n * back to a recursive search, so one folder string works across operations. A\n * missing folder fails as NOT_FOUND naming what the caller typed and what the\n * deepest folder reached does hold — never a silent fall back to the root. With\n * `createMissing`, the whole missing chain is created instead.\n */\nexport function mailScopeScript(ref: MailFolderRef, folderLabel: string, createMissing = false): string {\n const root = `$store.GetDefaultFolder(${psInt(ref.rootId)})`;\n if (ref.segments.length === 0) return `$scope = ${root}\\n$scopeCreated = $false`;\n return `\n${FIND_FOLDER_PS}\n$folderLabel = ${psString(folderLabel)}\n$segments = @(${ref.segments.map(psString).join(', ')})\n$scopeRoot = ${root}\n$scope = $scopeRoot\n$deepest = $scopeRoot\n$scopeCreated = $false\nforeach ($seg in $segments) {\n $next = $null\n foreach ($f in $scope.Folders) { if ($f.Name -ieq $seg) { $next = $f; break } }\n if ($next -eq $null) { $deepest = $scope; $scope = $null; break }\n $scope = $next\n}\nif ($scope -eq $null -and $segments.Count -eq 1) {\n $scope = Find-FolderByName $scopeRoot $segments[0] 3\n}\nif ($scope -eq $null -and ${createMissing ? '$true' : '$false'}) {\n # Rebuild the chain from the root, creating only what is genuinely absent.\n $scope = $scopeRoot\n foreach ($seg in $segments) {\n $next = $null\n foreach ($f in $scope.Folders) { if ($f.Name -ieq $seg) { $next = $f; break } }\n if ($next -eq $null) { $next = $scope.Folders.Add($seg); $scopeCreated = $true }\n $scope = $next\n }\n}\nif ($scope -eq $null) {\n $names = @()\n foreach ($f in $deepest.Folders) { $names += $f.Name }\n throw \"${failureTag('NOT_FOUND', 'folder')}Folder '$folderLabel' not found. Folders under '$($deepest.Name)': $($names -join ', ')\"\n}`;\n}\n"]}
@@ -1,11 +1,6 @@
1
- import type { ReplyEmailParams, ReplyEmailResult, SendEmailParams } from '../types';
2
- /** Send (or display for review) an email through Outlook COM. */
3
- export declare function sendOutlookEmail(params: SendEmailParams): Promise<void>;
4
- /**
5
- * Reply to an email, inserting the caller's HTML above the quoted original.
6
- *
7
- * The body may be given directly or named as a saved template — see
8
- * `composeReplyHtml`, which resolves the template, its section and its
9
- * placeholders before anything reaches Outlook.
10
- */
11
- export declare function replyOutlookEmail(params: ReplyEmailParams): Promise<ReplyEmailResult>;
1
+ import type { ReplyRequest, SendRequest } from '../backend';
2
+ import type { ReplyEmailResult } from '../types';
3
+ /** Send, display or file a new email. */
4
+ export declare function sendOutlookEmail(request: SendRequest): Promise<void>;
5
+ /** Reply to an email, with the composed HTML above the quoted original. */
6
+ export declare function replyOutlookEmail(request: ReplyRequest): Promise<ReplyEmailResult>;