@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
@@ -6,120 +6,108 @@ exports.replyOutlookEmail = replyOutlookEmail;
6
6
  const run_1 = require("./run");
7
7
  const scripts_1 = require("./scripts");
8
8
  const replyBody_1 = require("../shared/replyBody");
9
+ const errors_1 = require("../errors");
9
10
  const json_1 = require("../shared/json");
10
- const signatures_1 = require("./signatures");
11
- const templates_1 = require("./templates");
12
11
  /**
13
12
  * Set the sending account through IDispatch reflection.
14
13
  *
15
- * Direct assignment (`$mail.SendUsingAccount = $account`) is a silent no-op under
14
+ * Plain assignment (`$mail.SendUsingAccount = $account`) is a silent no-op under
16
15
  * PowerShell's COM binding — the mail then goes out from whichever account
17
- * Outlook considers default, which is the failure this package exists to prevent.
16
+ * Outlook considers default, which is exactly what this package must never do.
18
17
  *
19
- * Refuses a mailbox that resolved to a store with no account. `accountScript`
20
- * admits those deliberately so the reads can reach them, but SendUsingAccount
21
- * takes an Account and there is no store to substitute: sending anyway would
22
- * leave $account null and fall through to the default account — the exact silent
23
- * wrong-mailbox send this function exists to stop. The guard lives here rather
24
- * than at the two call sites so neither can be added without it.
25
- *
26
- * The wording avoids the phrase `classifyRunFailure` matches for
27
- * AccountNotFoundError; the mailbox WAS found, it just cannot send.
18
+ * Refuses a mailbox that resolved to a store with no Account: SendUsingAccount
19
+ * takes an Account, and sending with none falls through to the default account.
20
+ * The usual cause is a stale session — Outlook builds its Accounts list at
21
+ * startup, so a mailbox added since has its store mounted and readable but no
22
+ * Account until Outlook restarts — and the message says so.
28
23
  */
29
24
  function sendUsingAccount(variable) {
30
- return `if ($account -eq $null) { throw "Mailbox '$target' is a secondary store with no sending account attached." }
25
+ return `if ($account -eq $null) {
26
+ throw "${(0, errors_1.failureTag)('INVALID_REQUEST')}Mailbox '$target' has no sending account in this Outlook session. If it was added to the profile after Outlook started, restart Outlook and try again."
27
+ }
31
28
  [void]$${variable}.GetType().InvokeMember('SendUsingAccount', [Reflection.BindingFlags]::SetProperty, $null, $${variable}, @($account))`;
32
29
  }
33
30
  /**
34
- * What to do with a composed item: send it, show it, or file it silently.
35
- *
36
- * `Display()` only shows the compose window — it never files the item, so a
37
- * windowless draft has to `Save()` into Drafts explicitly.
31
+ * What to do with a composed item. `Display()` only shows a compose window and
32
+ * never files the item, so a windowless draft has to `Save()` into Drafts.
38
33
  */
39
- function composeAction(variable, sendImmediately, openDraftWindow) {
40
- if (sendImmediately)
41
- return `$${variable}.Send()`;
42
- return openDraftWindow !== false ? `$${variable}.Display()` : `$${variable}.Save()`;
34
+ function dispose(variable, disposition) {
35
+ switch (disposition) {
36
+ case 'send':
37
+ return `$${variable}.Send()`;
38
+ case 'display':
39
+ return `$${variable}.Display()`;
40
+ case 'save':
41
+ return `$${variable}.Save()`;
42
+ }
43
43
  }
44
- /** Send (or display for review) an email through Outlook COM. */
45
- async function sendOutlookEmail(params) {
46
- (0, run_1.requireWindows)();
47
- const attachLine = params.attachmentPath
48
- ? `$mail.Attachments.Add('${(0, run_1.psEscape)(params.attachmentPath)}') | Out-Null`
49
- : '';
50
- // The HTML body goes through a file: inline, a large email is what pushes the
51
- // generated script past the command-line length limit.
52
- const body = (0, run_1.scriptInput)('email-body', params.htmlBody);
53
- const script = `${(0, scripts_1.accountScript)(params.emailAccount)}
44
+ /** Outlook takes recipients as one semicolon-separated line. */
45
+ function recipientLine(entries) {
46
+ return (0, run_1.psString)(entries.join('; '));
47
+ }
48
+ /**
49
+ * Send, display or file a new email. Only a filed draft reports an id: a sent
50
+ * item has left for the Outbox, and a displayed one is saved nowhere yet. The
51
+ * id is read after `Save()`, which is what assigns it — the EntryID the
52
+ * draft then keeps, and the one listOutlookDrafts reports.
53
+ */
54
+ async function sendOutlookEmail(request) {
55
+ // The body goes through a file: inline, a large email bloats the script and
56
+ // every error that carries it.
57
+ const body = (0, run_1.scriptInput)('email-body', request.htmlBody);
58
+ const attach = request.attachments
59
+ .map(file => `[void]$mail.Attachments.Add(${(0, run_1.psString)(file)})`)
60
+ .join('\n');
61
+ const draftEntryId = request.disposition === 'save' ? '[string]$mail.EntryID' : "''";
62
+ try {
63
+ const output = await (0, run_1.runPowerShellJson)(`${(0, scripts_1.accountScript)(request.account)}
54
64
  $mail = $outlook.CreateItem(0)
55
65
  ${sendUsingAccount('mail')}
56
- $mail.To = '${(0, run_1.psEscape)(params.to)}'
57
- $mail.CC = '${(0, run_1.psEscape)(params.cc || '')}'
58
- $mail.Subject = '${(0, run_1.psEscape)(params.subject)}'
59
- $mail.HTMLBody = [IO.File]::ReadAllText('${(0, run_1.psEscape)(body.path)}', [Text.Encoding]::UTF8)
60
- ${attachLine}
61
- ${composeAction('mail', params.sendImmediately, params.openDraftWindow)}
62
- `;
63
- try {
64
- await (0, run_1.runPowerShellFile)(script, 120000);
66
+ $mail.To = ${recipientLine(request.to)}
67
+ $mail.CC = ${recipientLine(request.cc)}
68
+ $mail.BCC = ${recipientLine(request.bcc)}
69
+ $mail.Subject = ${(0, run_1.psString)(request.subject)}
70
+ $mail.HTMLBody = [IO.File]::ReadAllText(${(0, run_1.psString)(body.path)}, [Text.Encoding]::UTF8)
71
+ ${attach}
72
+ ${dispose('mail', request.disposition)}
73
+ ConvertTo-Json -Compress @{ draftEntryId = ${draftEntryId} }
74
+ `, 'standard');
75
+ return { draftEntryId: (0, json_1.str)((0, json_1.record)(output).draftEntryId) };
65
76
  }
66
77
  finally {
67
78
  body.cleanup();
68
79
  }
69
80
  }
70
- /**
71
- * Reply to an email, inserting the caller's HTML above the quoted original.
72
- *
73
- * The body may be given directly or named as a saved template — see
74
- * `composeReplyHtml`, which resolves the template, its section and its
75
- * placeholders before anything reaches Outlook.
76
- */
77
- async function replyOutlookEmail(params) {
78
- (0, run_1.requireWindows)();
79
- const insertHtml = await (0, replyBody_1.composeReplyHtml)(params, {
80
- readTemplateEmails: templates_1.readTemplateEmails,
81
- readOutlookSignatureHtml: signatures_1.readOutlookSignatureHtml,
82
- listOutlookSignatures: signatures_1.listOutlookSignatures,
83
- });
84
- const body = (0, run_1.scriptInput)('reply-insert', insertHtml);
85
- const replyMethod = params.replyAll ? 'ReplyAll' : 'Reply';
86
- // Outlook builds the reply as a Word document whose own empty paragraph sits
87
- // inside WordSection1; inserting straight after that div's opening tag puts
88
- // the new text where the user's cursor would have been. A reply Outlook did
89
- // not build that way falls back to just inside <body>.
90
- const script = `${(0, scripts_1.accountScript)(params.emailAccount)}
91
- ${(0, scripts_1.getItemScript)(params.entryId, params.storeId)}
92
- $reply = $item.${replyMethod}()
81
+ /** Reply to an email, with the composed HTML above the quoted original. */
82
+ async function replyOutlookEmail(request) {
83
+ const insert = (0, run_1.scriptInput)('reply-insert', request.html);
84
+ try {
85
+ const output = await (0, run_1.runPowerShellJson)(`${(0, scripts_1.accountScript)(request.account)}
86
+ ${(0, scripts_1.itemLookupScript)(request.email)}
87
+ $reply = $item.${request.replyAll ? 'ReplyAll' : 'Reply'}()
93
88
  ${sendUsingAccount('reply')}
94
-
95
- $insertHtml = [IO.File]::ReadAllText('${(0, run_1.psEscape)(body.path)}', [Text.Encoding]::UTF8)
96
- $keyWord = '${(0, run_1.psEscape)(replyBody_1.WORD_SECTION_ANCHOR)}'
97
- # Bound to a local first: each $reply.HTMLBody read marshals the WHOLE generated
98
- # reply across COM, and a Word reply on a long thread runs to hundreds of KB.
99
- $html = $reply.HTMLBody
100
- $idx = $html.IndexOf($keyWord)
101
- if ($idx -ge 0) { $idx += $keyWord.Length } else {
102
- $m = [regex]::Match($html, '<body[^>]*>')
103
- $idx = if ($m.Success) { $m.Index + $m.Length } else { 0 }
89
+ $insertHtml = [IO.File]::ReadAllText(${(0, run_1.psString)(insert.path)}, [Text.Encoding]::UTF8)
90
+ $anchor = ${(0, run_1.psString)(replyBody_1.WORD_SECTION_ANCHOR)}
91
+ # Read once: every HTMLBody read marshals the whole generated reply across COM,
92
+ # and a reply on a long thread runs to hundreds of KB.
93
+ $html = [string]$reply.HTMLBody
94
+ $at = $html.IndexOf($anchor)
95
+ if ($at -ge 0) { $at += $anchor.Length } else {
96
+ $bodyTag = [regex]::Match($html, '<body[^>]*>')
97
+ $at = if ($bodyTag.Success) { $bodyTag.Index + $bodyTag.Length } else { 0 }
104
98
  }
105
- $reply.HTMLBody = $html.Insert($idx, $insertHtml)
99
+ $reply.HTMLBody = $html.Insert($at, $insertHtml)
106
100
  $to = [string]$reply.To
107
101
  $subject = [string]$reply.Subject
108
- $sender = ''
109
- try { $sender = [string]$item.SenderEmailAddress } catch {}
110
- ${composeAction('reply', params.sendImmediately, params.openDraftWindow)}
111
- ConvertTo-Json @{ to = $to; subject = $subject; repliedToSender = $sender }
112
- `;
113
- try {
114
- const parsed = (0, json_1.parseObject)(await (0, run_1.runPowerShell)(script, 60000));
115
- return {
116
- to: (0, json_1.str)(parsed.to),
117
- subject: (0, json_1.str)(parsed.subject),
118
- repliedToSender: (0, json_1.str)(parsed.repliedToSender),
119
- };
102
+ ${scripts_1.SENDER_SMTP_PS}
103
+ ${dispose('reply', request.disposition)}
104
+ ConvertTo-Json -Compress @{ to = $to; subject = $subject; repliedToSender = $senderSmtp }
105
+ `, 'standard');
106
+ const result = (0, json_1.record)(output);
107
+ return { to: (0, json_1.str)(result.to), subject: (0, json_1.str)(result.subject), repliedToSender: (0, json_1.str)(result.repliedToSender) };
120
108
  }
121
109
  finally {
122
- body.cleanup();
110
+ insert.cleanup();
123
111
  }
124
112
  }
125
113
  //# sourceMappingURL=send.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"send.js","sourceRoot":"","sources":["../../src/windows/send.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"send.js","sourceRoot":"","sources":["../../src/windows/send.ts"],"names":[],"mappings":"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","sourcesContent":["// Composing outgoing mail: a new email, and a reply to an existing one.\nimport { psString, runPowerShellJson, scriptInput } from './run';\nimport { accountScript, itemLookupScript, SENDER_SMTP_PS } from './scripts';\nimport { WORD_SECTION_ANCHOR } from '../shared/replyBody';\nimport { failureTag } from '../errors';\nimport { record, str } from '../shared/json';\nimport type { Disposition, ReplyRequest, SendRequest } from '../backend';\nimport type { ReplyEmailResult, SendEmailResult } from '../types';\n\n/**\n * Set the sending account through IDispatch reflection.\n *\n * Plain assignment (`$mail.SendUsingAccount = $account`) is a silent no-op under\n * PowerShell's COM binding — the mail then goes out from whichever account\n * Outlook considers default, which is exactly what this package must never do.\n *\n * Refuses a mailbox that resolved to a store with no Account: SendUsingAccount\n * takes an Account, and sending with none falls through to the default account.\n * The usual cause is a stale session — Outlook builds its Accounts list at\n * startup, so a mailbox added since has its store mounted and readable but no\n * Account until Outlook restarts — and the message says so.\n */\nfunction sendUsingAccount(variable: string): string {\n return `if ($account -eq $null) {\n throw \"${failureTag('INVALID_REQUEST')}Mailbox '$target' has no sending account in this Outlook session. If it was added to the profile after Outlook started, restart Outlook and try again.\"\n}\n[void]$${variable}.GetType().InvokeMember('SendUsingAccount', [Reflection.BindingFlags]::SetProperty, $null, $${variable}, @($account))`;\n}\n\n/**\n * What to do with a composed item. `Display()` only shows a compose window and\n * never files the item, so a windowless draft has to `Save()` into Drafts.\n */\nfunction dispose(variable: string, disposition: Disposition): string {\n switch (disposition) {\n case 'send':\n return `$${variable}.Send()`;\n case 'display':\n return `$${variable}.Display()`;\n case 'save':\n return `$${variable}.Save()`;\n }\n}\n\n/** Outlook takes recipients as one semicolon-separated line. */\nfunction recipientLine(entries: readonly string[]): string {\n return psString(entries.join('; '));\n}\n\n/**\n * Send, display or file a new email. Only a filed draft reports an id: a sent\n * item has left for the Outbox, and a displayed one is saved nowhere yet. The\n * id is read after `Save()`, which is what assigns it — the EntryID the\n * draft then keeps, and the one listOutlookDrafts reports.\n */\nexport async function sendOutlookEmail(request: SendRequest): Promise<SendEmailResult> {\n // The body goes through a file: inline, a large email bloats the script and\n // every error that carries it.\n const body = scriptInput('email-body', request.htmlBody);\n const attach = request.attachments\n .map(file => `[void]$mail.Attachments.Add(${psString(file)})`)\n .join('\\n');\n const draftEntryId = request.disposition === 'save' ? '[string]$mail.EntryID' : \"''\";\n try {\n const output = await runPowerShellJson(`${accountScript(request.account)}\n$mail = $outlook.CreateItem(0)\n${sendUsingAccount('mail')}\n$mail.To = ${recipientLine(request.to)}\n$mail.CC = ${recipientLine(request.cc)}\n$mail.BCC = ${recipientLine(request.bcc)}\n$mail.Subject = ${psString(request.subject)}\n$mail.HTMLBody = [IO.File]::ReadAllText(${psString(body.path)}, [Text.Encoding]::UTF8)\n${attach}\n${dispose('mail', request.disposition)}\nConvertTo-Json -Compress @{ draftEntryId = ${draftEntryId} }\n`, 'standard');\n return { draftEntryId: str(record(output).draftEntryId) };\n } finally {\n body.cleanup();\n }\n}\n\n/** Reply to an email, with the composed HTML above the quoted original. */\nexport async function replyOutlookEmail(request: ReplyRequest): Promise<ReplyEmailResult> {\n const insert = scriptInput('reply-insert', request.html);\n try {\n const output = await runPowerShellJson(`${accountScript(request.account)}\n${itemLookupScript(request.email)}\n$reply = $item.${request.replyAll ? 'ReplyAll' : 'Reply'}()\n${sendUsingAccount('reply')}\n$insertHtml = [IO.File]::ReadAllText(${psString(insert.path)}, [Text.Encoding]::UTF8)\n$anchor = ${psString(WORD_SECTION_ANCHOR)}\n# Read once: every HTMLBody read marshals the whole generated reply across COM,\n# and a reply on a long thread runs to hundreds of KB.\n$html = [string]$reply.HTMLBody\n$at = $html.IndexOf($anchor)\nif ($at -ge 0) { $at += $anchor.Length } else {\n $bodyTag = [regex]::Match($html, '<body[^>]*>')\n $at = if ($bodyTag.Success) { $bodyTag.Index + $bodyTag.Length } else { 0 }\n}\n$reply.HTMLBody = $html.Insert($at, $insertHtml)\n$to = [string]$reply.To\n$subject = [string]$reply.Subject\n${SENDER_SMTP_PS}\n${dispose('reply', request.disposition)}\nConvertTo-Json -Compress @{ to = $to; subject = $subject; repliedToSender = $senderSmtp }\n`, 'standard');\n const result = record(output);\n return { to: str(result.to), subject: str(result.subject), repliedToSender: str(result.repliedToSender) };\n } finally {\n insert.cleanup();\n }\n}\n"]}
@@ -1,14 +1,8 @@
1
- /**
2
- * Names of the user's Outlook signatures (the ".htm" files), sorted. Empty off
3
- * Windows. Reads from disk synchronously but is declared async to match macOS,
4
- * which has to ask Outlook itself — one signature for both platforms.
5
- */
1
+ /** The signature names — the ".htm" files — sorted. */
6
2
  export declare function listOutlookSignatures(): Promise<string[]>;
7
3
  /**
8
- * Read a named signature's HTML with its image references rewritten to absolute
9
- * file: URIs. Outlook stores signature images relative to a "<name>_files"
10
- * folder; once the refs are absolute, assigning the HTML to a mail body lets
11
- * Outlook resolve and embed the images on Display/Send. Returns '' if the
12
- * signature can't be found.
4
+ * A signature's HTML with its image references made absolute file: URIs, so an
5
+ * email body built from it resolves and embeds the images; '' when there is no
6
+ * signature by that name.
13
7
  */
14
8
  export declare function readOutlookSignatureHtml(name: string): Promise<string>;
@@ -5,27 +5,24 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.listOutlookSignatures = listOutlookSignatures;
7
7
  exports.readOutlookSignatureHtml = readOutlookSignatureHtml;
8
- // Outlook stores each signature as "<name>.htm" (plus .rtf/.txt and a
9
- // "<name>_files" folder for images) under %APPDATA%\Microsoft\Signatures, so
10
- // both of these read the disk rather than driving COM.
8
+ // Outlook for Windows keeps each signature as "<name>.htm" (beside .rtf/.txt
9
+ // copies and a "<name>_files" folder of images) under
10
+ // %APPDATA%\Microsoft\Signatures, so these read the disk rather than COM.
11
11
  const fs_1 = __importDefault(require("fs"));
12
12
  const path_1 = __importDefault(require("path"));
13
13
  const url_1 = require("url");
14
- const SIGNATURES_DIR = process.env.APPDATA
15
- ? path_1.default.join(process.env.APPDATA, 'Microsoft', 'Signatures')
16
- : '';
17
- /**
18
- * Names of the user's Outlook signatures (the ".htm" files), sorted. Empty off
19
- * Windows. Reads from disk synchronously but is declared async to match macOS,
20
- * which has to ask Outlook itself — one signature for both platforms.
21
- */
14
+ function signaturesDir() {
15
+ return process.env.APPDATA ? path_1.default.join(process.env.APPDATA, 'Microsoft', 'Signatures') : '';
16
+ }
17
+ /** The signature names — the ".htm" files — sorted. */
22
18
  async function listOutlookSignatures() {
23
- if (process.platform !== 'win32' || !SIGNATURES_DIR)
19
+ const dir = signaturesDir();
20
+ if (!dir)
24
21
  return [];
25
22
  try {
26
- return fs_1.default.readdirSync(SIGNATURES_DIR)
27
- .filter(f => f.toLowerCase().endsWith('.htm'))
28
- .map(f => f.slice(0, -4))
23
+ return fs_1.default.readdirSync(dir)
24
+ .filter(file => file.toLowerCase().endsWith('.htm'))
25
+ .map(file => file.slice(0, -'.htm'.length))
29
26
  .sort((a, b) => a.localeCompare(b));
30
27
  }
31
28
  catch {
@@ -33,37 +30,33 @@ async function listOutlookSignatures() {
33
30
  }
34
31
  }
35
32
  /**
36
- * Read a named signature's HTML with its image references rewritten to absolute
37
- * file: URIs. Outlook stores signature images relative to a "<name>_files"
38
- * folder; once the refs are absolute, assigning the HTML to a mail body lets
39
- * Outlook resolve and embed the images on Display/Send. Returns '' if the
40
- * signature can't be found.
33
+ * A signature's HTML with its image references made absolute file: URIs, so an
34
+ * email body built from it resolves and embeds the images; '' when there is no
35
+ * signature by that name.
41
36
  */
42
37
  async function readOutlookSignatureHtml(name) {
43
- if (process.platform !== 'win32' || !SIGNATURES_DIR)
38
+ const dir = signaturesDir();
39
+ if (!dir)
44
40
  return '';
45
- // Only accept a bare signature name — never a path — so a crafted name can't
46
- // escape the Signatures folder.
47
- const safe = path_1.default.basename(name);
48
- const file = path_1.default.join(SIGNATURES_DIR, `${safe}.htm`);
41
+ // A bare name only — never a path — so a crafted name can't reach outside
42
+ // the Signatures folder.
43
+ const file = path_1.default.join(dir, `${path_1.default.basename(name)}.htm`);
49
44
  if (!fs_1.default.existsSync(file))
50
45
  return '';
51
- // Classic Outlook signatures are saved as windows-1252, not UTF-8; decode by
52
- // the charset the file declares so accented text / smart quotes survive.
53
- const buf = fs_1.default.readFileSync(file);
54
- const head = buf.toString('latin1', 0, 2048);
55
- const charset = head.match(/charset=["']?([\w-]+)/i)?.[1] || 'utf-8';
46
+ // Signatures are saved in whatever charset Outlook chose (often
47
+ // windows-1252), so decode by the one the file declares.
48
+ const bytes = fs_1.default.readFileSync(file);
49
+ const charset = bytes.toString('latin1', 0, 2048).match(/charset=["']?([\w-]+)/i)?.[1] || 'utf-8';
56
50
  let html;
57
51
  try {
58
- html = new TextDecoder(charset).decode(buf);
52
+ html = new TextDecoder(charset).decode(bytes);
59
53
  }
60
54
  catch {
61
- html = buf.toString('utf-8');
55
+ html = bytes.toString('utf8');
62
56
  }
63
- // pathToFileURL encodes spaces/specials the same way Outlook's relative refs
64
- // are, so prefixing the (already relative, already-encoded) src keeps a valid URI.
65
- const dirUri = (0, url_1.pathToFileURL)(SIGNATURES_DIR + path_1.default.sep).href;
66
- html = html.replace(/(src|background)=(["'])(?!https?:|cid:|data:|file:|mailto:|#)/gi, `$1=$2${dirUri}`);
67
- return html;
57
+ // pathToFileURL encodes the directory the way Outlook's relative refs are
58
+ // already encoded, so prefixing it keeps each reference a valid URI.
59
+ const dirUri = (0, url_1.pathToFileURL)(dir + path_1.default.sep).href;
60
+ return html.replace(/(src|background)=(["'])(?!https?:|cid:|data:|file:|mailto:|#)/gi, `$1=$2${dirUri}`);
68
61
  }
69
62
  //# sourceMappingURL=signatures.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"signatures.js","sourceRoot":"","sources":["../../src/windows/signatures.ts"],"names":[],"mappings":";;;;;AAgBA,sDAUC;AASD,4DA0BC;AA7DD,sEAAsE;AACtE,6EAA6E;AAC7E,uDAAuD;AACvD,4CAAoB;AACpB,gDAAwB;AACxB,6BAAoC;AAEpC,MAAM,cAAc,GAAG,OAAO,CAAC,GAAG,CAAC,OAAO;IACtC,CAAC,CAAC,cAAI,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,OAAO,EAAE,WAAW,EAAE,YAAY,CAAC;IAC3D,CAAC,CAAC,EAAE,CAAC;AAET;;;;GAIG;AACI,KAAK,UAAU,qBAAqB;IACvC,IAAI,OAAO,CAAC,QAAQ,KAAK,OAAO,IAAI,CAAC,cAAc;QAAE,OAAO,EAAE,CAAC;IAC/D,IAAI,CAAC;QACD,OAAO,YAAE,CAAC,WAAW,CAAC,cAAc,CAAC;aAChC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;aAC7C,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;aACxB,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,CAAC;IAC5C,CAAC;IAAC,MAAM,CAAC;QACL,OAAO,EAAE,CAAC;IACd,CAAC;AACL,CAAC;AAED;;;;;;GAMG;AACI,KAAK,UAAU,wBAAwB,CAAC,IAAY;IACvD,IAAI,OAAO,CAAC,QAAQ,KAAK,OAAO,IAAI,CAAC,cAAc;QAAE,OAAO,EAAE,CAAC;IAC/D,6EAA6E;IAC7E,gCAAgC;IAChC,MAAM,IAAI,GAAG,cAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;IACjC,MAAM,IAAI,GAAG,cAAI,CAAC,IAAI,CAAC,cAAc,EAAE,GAAG,IAAI,MAAM,CAAC,CAAC;IACtD,IAAI,CAAC,YAAE,CAAC,UAAU,CAAC,IAAI,CAAC;QAAE,OAAO,EAAE,CAAC;IACpC,6EAA6E;IAC7E,yEAAyE;IACzE,MAAM,GAAG,GAAG,YAAE,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC;IAClC,MAAM,IAAI,GAAG,GAAG,CAAC,QAAQ,CAAC,QAAQ,EAAE,CAAC,EAAE,IAAI,CAAC,CAAC;IAC7C,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,wBAAwB,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,OAAO,CAAC;IACrE,IAAI,IAAY,CAAC;IACjB,IAAI,CAAC;QACD,IAAI,GAAG,IAAI,WAAW,CAAC,OAAO,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;IAChD,CAAC;IAAC,MAAM,CAAC;QACL,IAAI,GAAG,GAAG,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;IACjC,CAAC;IACD,6EAA6E;IAC7E,mFAAmF;IACnF,MAAM,MAAM,GAAG,IAAA,mBAAa,EAAC,cAAc,GAAG,cAAI,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC;IAC7D,IAAI,GAAG,IAAI,CAAC,OAAO,CACf,iEAAiE,EACjE,QAAQ,MAAM,EAAE,CACnB,CAAC;IACF,OAAO,IAAI,CAAC;AAChB,CAAC"}
1
+ {"version":3,"file":"signatures.js","sourceRoot":"","sources":["../../src/windows/signatures.ts"],"names":[],"mappings":";;;;;AAYA,sDAWC;AAOD,4DAqBC;AAnDD,6EAA6E;AAC7E,sDAAsD;AACtD,0EAA0E;AAC1E,4CAAoB;AACpB,gDAAwB;AACxB,6BAAoC;AAEpC,SAAS,aAAa;IAClB,OAAO,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,cAAI,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,OAAO,EAAE,WAAW,EAAE,YAAY,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;AAChG,CAAC;AAED,uDAAuD;AAChD,KAAK,UAAU,qBAAqB;IACvC,MAAM,GAAG,GAAG,aAAa,EAAE,CAAC;IAC5B,IAAI,CAAC,GAAG;QAAE,OAAO,EAAE,CAAC;IACpB,IAAI,CAAC;QACD,OAAO,YAAE,CAAC,WAAW,CAAC,GAAG,CAAC;aACrB,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;aACnD,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;aAC1C,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,CAAC;IAC5C,CAAC;IAAC,MAAM,CAAC;QACL,OAAO,EAAE,CAAC;IACd,CAAC;AACL,CAAC;AAED;;;;GAIG;AACI,KAAK,UAAU,wBAAwB,CAAC,IAAY;IACvD,MAAM,GAAG,GAAG,aAAa,EAAE,CAAC;IAC5B,IAAI,CAAC,GAAG;QAAE,OAAO,EAAE,CAAC;IACpB,0EAA0E;IAC1E,yBAAyB;IACzB,MAAM,IAAI,GAAG,cAAI,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,cAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IAC1D,IAAI,CAAC,YAAE,CAAC,UAAU,CAAC,IAAI,CAAC;QAAE,OAAO,EAAE,CAAC;IACpC,gEAAgE;IAChE,yDAAyD;IACzD,MAAM,KAAK,GAAG,YAAE,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC;IACpC,MAAM,OAAO,GAAG,KAAK,CAAC,QAAQ,CAAC,QAAQ,EAAE,CAAC,EAAE,IAAI,CAAC,CAAC,KAAK,CAAC,wBAAwB,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,OAAO,CAAC;IAClG,IAAI,IAAY,CAAC;IACjB,IAAI,CAAC;QACD,IAAI,GAAG,IAAI,WAAW,CAAC,OAAO,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IAClD,CAAC;IAAC,MAAM,CAAC;QACL,IAAI,GAAG,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;IAClC,CAAC;IACD,0EAA0E;IAC1E,qEAAqE;IACrE,MAAM,MAAM,GAAG,IAAA,mBAAa,EAAC,GAAG,GAAG,cAAI,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC;IAClD,OAAO,IAAI,CAAC,OAAO,CAAC,iEAAiE,EAAE,QAAQ,MAAM,EAAE,CAAC,CAAC;AAC7G,CAAC","sourcesContent":["// Outlook for Windows keeps each signature as \"<name>.htm\" (beside .rtf/.txt\n// copies and a \"<name>_files\" folder of images) under\n// %APPDATA%\\Microsoft\\Signatures, so these read the disk rather than COM.\nimport fs from 'fs';\nimport path from 'path';\nimport { pathToFileURL } from 'url';\n\nfunction signaturesDir(): string {\n return process.env.APPDATA ? path.join(process.env.APPDATA, 'Microsoft', 'Signatures') : '';\n}\n\n/** The signature names — the \".htm\" files — sorted. */\nexport async function listOutlookSignatures(): Promise<string[]> {\n const dir = signaturesDir();\n if (!dir) return [];\n try {\n return fs.readdirSync(dir)\n .filter(file => file.toLowerCase().endsWith('.htm'))\n .map(file => file.slice(0, -'.htm'.length))\n .sort((a, b) => a.localeCompare(b));\n } catch {\n return [];\n }\n}\n\n/**\n * A signature's HTML with its image references made absolute file: URIs, so an\n * email body built from it resolves and embeds the images; '' when there is no\n * signature by that name.\n */\nexport async function readOutlookSignatureHtml(name: string): Promise<string> {\n const dir = signaturesDir();\n if (!dir) return '';\n // A bare name only — never a path — so a crafted name can't reach outside\n // the Signatures folder.\n const file = path.join(dir, `${path.basename(name)}.htm`);\n if (!fs.existsSync(file)) return '';\n // Signatures are saved in whatever charset Outlook chose (often\n // windows-1252), so decode by the one the file declares.\n const bytes = fs.readFileSync(file);\n const charset = bytes.toString('latin1', 0, 2048).match(/charset=[\"']?([\\w-]+)/i)?.[1] || 'utf-8';\n let html: string;\n try {\n html = new TextDecoder(charset).decode(bytes);\n } catch {\n html = bytes.toString('utf8');\n }\n // pathToFileURL encodes the directory the way Outlook's relative refs are\n // already encoded, so prefixing it keeps each reference a valid URI.\n const dirUri = pathToFileURL(dir + path.sep).href;\n return html.replace(/(src|background)=([\"'])(?!https?:|cid:|data:|file:|mailto:|#)/gi, `$1=$2${dirUri}`);\n}\n"]}
@@ -1,25 +1,19 @@
1
+ import type { EditTemplateRequest, ReadTemplatesRequest, SaveTemplateRequest } from '../backend';
1
2
  import type { SaveTemplateResult, TemplateFolderResult } from '../types';
2
3
  /**
3
- * Read the template emails saved in a mailbox folder (default "Templates"),
4
- * returning each item's full HTML body. When the folder doesn't exist, returns
5
- * folderFound:false plus the mailbox's folder names instead of throwing, so the
6
- * caller can ask the user whether to create it rather than fail.
4
+ * The template emails in a mailbox folder, newest first. A missing folder is
5
+ * reported as folderFound:false with the mailbox's top-level folder names
6
+ * rather than thrown, so a caller can offer to create it.
7
7
  */
8
- export declare function readTemplateEmails(emailAccount: string, folderName?: string, limit?: number, includeBody?: boolean, subject?: string): Promise<TemplateFolderResult>;
8
+ export declare function readTemplateEmails(request: ReadTemplatesRequest): Promise<TemplateFolderResult>;
9
9
  /**
10
- * Save a new template email into a mailbox folder (default "Templates"),
11
- * creating the folder at the mailbox root if it doesn't exist. The item is a
12
- * plain unsent mail (subject + HTML body) that can be edited in Outlook.
13
- * Never overwrites an existing item — it always adds a new one, so check with
14
- * readTemplateEmails first and only call after the user has agreed to create it.
10
+ * Save a new template email into a folder, creating the folder at the mailbox
11
+ * root when absent. Always adds a new item — never overwrites one.
15
12
  */
16
- export declare function saveTemplateEmail(emailAccount: string, subject: string, htmlBody: string, folderName?: string): Promise<SaveTemplateResult>;
13
+ export declare function saveTemplateEmail(request: SaveTemplateRequest): Promise<SaveTemplateResult>;
17
14
  /**
18
- * Open an Outlook compose window pre-filled with `currentHtml`, wait for the
19
- * user to close it, and return the HTML they saved.
20
- *
21
- * Deliberately unbounded: the run lasts as long as the person is editing. Give
22
- * it an AbortSignal (`bridge.withOptions({ signal })`) if the caller needs a way
23
- * to give up on them.
15
+ * Open a compose window pre-filled with the HTML, wait for it to close, and
16
+ * return what was saved (Ctrl+S) — or, failing a save, the body as it last
17
+ * stood while the window was open. Leftover editor drafts are removed.
24
18
  */
25
- export declare function editEmailTemplate(label: string, currentHtml: string): Promise<string>;
19
+ export declare function editEmailTemplate(request: EditTemplateRequest): Promise<string>;