@dawnlit/outlook-bridge 1.1.0 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (139) hide show
  1. package/README.md +150 -183
  2. package/dist/backend.d.ts +151 -0
  3. package/dist/backend.js +3 -0
  4. package/dist/backend.js.map +1 -0
  5. package/dist/bridge.d.ts +56 -0
  6. package/dist/bridge.js +507 -0
  7. package/dist/bridge.js.map +1 -0
  8. package/dist/errors.d.ts +81 -43
  9. package/dist/errors.js +98 -52
  10. package/dist/errors.js.map +1 -1
  11. package/dist/index.d.ts +10 -10
  12. package/dist/index.js +25 -52
  13. package/dist/index.js.map +1 -1
  14. package/dist/mac/accounts.d.ts +7 -2
  15. package/dist/mac/accounts.js +23 -6
  16. package/dist/mac/accounts.js.map +1 -1
  17. package/dist/mac/attachments.d.ts +5 -25
  18. package/dist/mac/attachments.js +40 -99
  19. package/dist/mac/attachments.js.map +1 -1
  20. package/dist/mac/bounces.d.ts +12 -27
  21. package/dist/mac/bounces.js +95 -112
  22. package/dist/mac/bounces.js.map +1 -1
  23. package/dist/mac/cleanup.d.ts +11 -25
  24. package/dist/mac/cleanup.js +50 -76
  25. package/dist/mac/cleanup.js.map +1 -1
  26. package/dist/mac/drafts.d.ts +13 -34
  27. package/dist/mac/drafts.js +95 -169
  28. package/dist/mac/drafts.js.map +1 -1
  29. package/dist/mac/folders.d.ts +10 -21
  30. package/dist/mac/folders.js +33 -47
  31. package/dist/mac/folders.js.map +1 -1
  32. package/dist/mac/index.d.ts +2 -22
  33. package/dist/mac/index.js +19 -81
  34. package/dist/mac/index.js.map +1 -1
  35. package/dist/mac/profile.d.ts +13 -0
  36. package/dist/mac/profile.js +149 -0
  37. package/dist/mac/profile.js.map +1 -0
  38. package/dist/mac/read.d.ts +18 -36
  39. package/dist/mac/read.js +154 -208
  40. package/dist/mac/read.js.map +1 -1
  41. package/dist/mac/run.d.ts +35 -39
  42. package/dist/mac/run.js +97 -102
  43. package/dist/mac/run.js.map +1 -1
  44. package/dist/mac/scripts.d.ts +78 -86
  45. package/dist/mac/scripts.js +125 -115
  46. package/dist/mac/scripts.js.map +1 -1
  47. package/dist/mac/send.d.ts +10 -20
  48. package/dist/mac/send.js +64 -86
  49. package/dist/mac/send.js.map +1 -1
  50. package/dist/mac/signatures.d.ts +2 -2
  51. package/dist/mac/signatures.js +9 -11
  52. package/dist/mac/signatures.js.map +1 -1
  53. package/dist/mac/templates.d.ts +14 -26
  54. package/dist/mac/templates.js +75 -113
  55. package/dist/mac/templates.js.map +1 -1
  56. package/dist/mail.d.ts +77 -45
  57. package/dist/mail.js +166 -73
  58. package/dist/mail.js.map +1 -1
  59. package/dist/runtime.d.ts +76 -43
  60. package/dist/runtime.js +103 -48
  61. package/dist/runtime.js.map +1 -1
  62. package/dist/shared/args.d.ts +48 -0
  63. package/dist/shared/args.js +216 -0
  64. package/dist/shared/args.js.map +1 -0
  65. package/dist/shared/attachmentMatch.d.ts +28 -16
  66. package/dist/shared/attachmentMatch.js +60 -22
  67. package/dist/shared/attachmentMatch.js.map +1 -1
  68. package/dist/shared/bounceReport.d.ts +11 -0
  69. package/dist/shared/bounceReport.js +45 -0
  70. package/dist/shared/bounceReport.js.map +1 -0
  71. package/dist/shared/bounceRules.d.ts +38 -10
  72. package/dist/shared/bounceRules.js +50 -32
  73. package/dist/shared/bounceRules.js.map +1 -1
  74. package/dist/shared/exec.d.ts +21 -0
  75. package/dist/shared/exec.js +47 -0
  76. package/dist/shared/exec.js.map +1 -0
  77. package/dist/shared/json.d.ts +12 -6
  78. package/dist/shared/json.js +29 -23
  79. package/dist/shared/json.js.map +1 -1
  80. package/dist/shared/locate.d.ts +39 -0
  81. package/dist/shared/locate.js +129 -0
  82. package/dist/shared/locate.js.map +1 -0
  83. package/dist/shared/replyBody.d.ts +42 -32
  84. package/dist/shared/replyBody.js +84 -71
  85. package/dist/shared/replyBody.js.map +1 -1
  86. package/dist/templateBody.d.ts +63 -0
  87. package/dist/{outlookTemplateSections.js → templateBody.js} +72 -72
  88. package/dist/templateBody.js.map +1 -0
  89. package/dist/types.d.ts +360 -165
  90. package/dist/types.js +8 -9
  91. package/dist/types.js.map +1 -1
  92. package/dist/windows/accounts.d.ts +10 -1
  93. package/dist/windows/accounts.js +25 -13
  94. package/dist/windows/accounts.js.map +1 -1
  95. package/dist/windows/attachments.d.ts +9 -24
  96. package/dist/windows/attachments.js +94 -119
  97. package/dist/windows/attachments.js.map +1 -1
  98. package/dist/windows/bounces.d.ts +13 -26
  99. package/dist/windows/bounces.js +169 -200
  100. package/dist/windows/bounces.js.map +1 -1
  101. package/dist/windows/cleanup.d.ts +19 -23
  102. package/dist/windows/cleanup.js +93 -111
  103. package/dist/windows/cleanup.js.map +1 -1
  104. package/dist/windows/drafts.d.ts +16 -35
  105. package/dist/windows/drafts.js +167 -253
  106. package/dist/windows/drafts.js.map +1 -1
  107. package/dist/windows/folders.d.ts +8 -13
  108. package/dist/windows/folders.js +45 -52
  109. package/dist/windows/folders.js.map +1 -1
  110. package/dist/windows/index.d.ts +2 -27
  111. package/dist/windows/index.js +11 -65
  112. package/dist/windows/index.js.map +1 -1
  113. package/dist/windows/read.d.ts +24 -45
  114. package/dist/windows/read.js +266 -279
  115. package/dist/windows/read.js.map +1 -1
  116. package/dist/windows/run.d.ts +39 -39
  117. package/dist/windows/run.js +151 -111
  118. package/dist/windows/run.js.map +1 -1
  119. package/dist/windows/scripts.d.ts +71 -38
  120. package/dist/windows/scripts.js +123 -71
  121. package/dist/windows/scripts.js.map +1 -1
  122. package/dist/windows/send.d.ts +6 -11
  123. package/dist/windows/send.js +70 -79
  124. package/dist/windows/send.js.map +1 -1
  125. package/dist/windows/signatures.d.ts +4 -10
  126. package/dist/windows/signatures.js +30 -37
  127. package/dist/windows/signatures.js.map +1 -1
  128. package/dist/windows/templates.d.ts +12 -18
  129. package/dist/windows/templates.js +126 -151
  130. package/dist/windows/templates.js.map +1 -1
  131. package/package.json +5 -4
  132. package/dist/OutlookService.d.ts +0 -43
  133. package/dist/OutlookService.js +0 -137
  134. package/dist/OutlookService.js.map +0 -1
  135. package/dist/outlookTemplateSections.d.ts +0 -62
  136. package/dist/outlookTemplateSections.js.map +0 -1
  137. package/dist/shared/capabilities.d.ts +0 -3
  138. package/dist/shared/capabilities.js +0 -9
  139. package/dist/shared/capabilities.js.map +0 -1
@@ -6,109 +6,100 @@ 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.
17
+ *
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.
18
23
  */
19
24
  function sendUsingAccount(variable) {
20
- return `[void]$${variable}.GetType().InvokeMember('SendUsingAccount', [Reflection.BindingFlags]::SetProperty, $null, $${variable}, @($account))`;
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
+ }
28
+ [void]$${variable}.GetType().InvokeMember('SendUsingAccount', [Reflection.BindingFlags]::SetProperty, $null, $${variable}, @($account))`;
21
29
  }
22
30
  /**
23
- * What to do with a composed item: send it, show it, or file it silently.
24
- *
25
- * `Display()` only shows the compose window — it never files the item, so a
26
- * 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.
27
33
  */
28
- function composeAction(variable, sendImmediately, openDraftWindow) {
29
- if (sendImmediately)
30
- return `$${variable}.Send()`;
31
- 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
+ }
32
43
  }
33
- /** Send (or display for review) an email through Outlook COM. */
34
- async function sendOutlookEmail(params) {
35
- (0, run_1.requireWindows)();
36
- const attachLine = params.attachmentPath
37
- ? `$mail.Attachments.Add('${(0, run_1.psEscape)(params.attachmentPath)}') | Out-Null`
38
- : '';
39
- // The HTML body goes through a file: inline, a large email is what pushes the
40
- // generated script past the command-line length limit.
41
- const body = (0, run_1.scriptInput)('email-body', params.htmlBody);
42
- 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
+ /** Send, display or file a new email. */
49
+ async function sendOutlookEmail(request) {
50
+ // The body goes through a file: inline, a large email bloats the script and
51
+ // every error that carries it.
52
+ const body = (0, run_1.scriptInput)('email-body', request.htmlBody);
53
+ const attach = request.attachments
54
+ .map(file => `[void]$mail.Attachments.Add(${(0, run_1.psString)(file)})`)
55
+ .join('\n');
56
+ try {
57
+ await (0, run_1.runPowerShell)(`${(0, scripts_1.accountScript)(request.account)}
43
58
  $mail = $outlook.CreateItem(0)
44
59
  ${sendUsingAccount('mail')}
45
- $mail.To = '${(0, run_1.psEscape)(params.to)}'
46
- $mail.CC = '${(0, run_1.psEscape)(params.cc || '')}'
47
- $mail.Subject = '${(0, run_1.psEscape)(params.subject)}'
48
- $mail.HTMLBody = [IO.File]::ReadAllText('${(0, run_1.psEscape)(body.path)}', [Text.Encoding]::UTF8)
49
- ${attachLine}
50
- ${composeAction('mail', params.sendImmediately, params.openDraftWindow)}
51
- `;
52
- try {
53
- await (0, run_1.runPowerShellFile)(script, 120000);
60
+ $mail.To = ${recipientLine(request.to)}
61
+ $mail.CC = ${recipientLine(request.cc)}
62
+ $mail.BCC = ${recipientLine(request.bcc)}
63
+ $mail.Subject = ${(0, run_1.psString)(request.subject)}
64
+ $mail.HTMLBody = [IO.File]::ReadAllText(${(0, run_1.psString)(body.path)}, [Text.Encoding]::UTF8)
65
+ ${attach}
66
+ ${dispose('mail', request.disposition)}
67
+ `, 'standard');
54
68
  }
55
69
  finally {
56
70
  body.cleanup();
57
71
  }
58
72
  }
59
- /**
60
- * Reply to an email, inserting the caller's HTML above the quoted original.
61
- *
62
- * The body may be given directly or named as a saved template — see
63
- * `composeReplyHtml`, which resolves the template, its section and its
64
- * placeholders before anything reaches Outlook.
65
- */
66
- async function replyOutlookEmail(params) {
67
- (0, run_1.requireWindows)();
68
- const insertHtml = await (0, replyBody_1.composeReplyHtml)(params, {
69
- readTemplateEmails: templates_1.readTemplateEmails,
70
- readOutlookSignatureHtml: signatures_1.readOutlookSignatureHtml,
71
- listOutlookSignatures: signatures_1.listOutlookSignatures,
72
- });
73
- const body = (0, run_1.scriptInput)('reply-insert', insertHtml);
74
- const replyMethod = params.replyAll ? 'ReplyAll' : 'Reply';
75
- // Outlook builds the reply as a Word document whose own empty paragraph sits
76
- // inside WordSection1; inserting straight after that div's opening tag puts
77
- // the new text where the user's cursor would have been. A reply Outlook did
78
- // not build that way falls back to just inside <body>.
79
- const script = `${(0, scripts_1.accountScript)(params.emailAccount)}
80
- ${(0, scripts_1.getItemScript)(params.entryId, params.storeId)}
81
- $reply = $item.${replyMethod}()
73
+ /** Reply to an email, with the composed HTML above the quoted original. */
74
+ async function replyOutlookEmail(request) {
75
+ const insert = (0, run_1.scriptInput)('reply-insert', request.html);
76
+ try {
77
+ const output = await (0, run_1.runPowerShellJson)(`${(0, scripts_1.accountScript)(request.account)}
78
+ ${(0, scripts_1.itemLookupScript)(request.email)}
79
+ $reply = $item.${request.replyAll ? 'ReplyAll' : 'Reply'}()
82
80
  ${sendUsingAccount('reply')}
83
-
84
- $insertHtml = [IO.File]::ReadAllText('${(0, run_1.psEscape)(body.path)}', [Text.Encoding]::UTF8)
85
- $keyWord = '${(0, run_1.psEscape)(replyBody_1.WORD_SECTION_ANCHOR)}'
86
- # Bound to a local first: each $reply.HTMLBody read marshals the WHOLE generated
87
- # reply across COM, and a Word reply on a long thread runs to hundreds of KB.
88
- $html = $reply.HTMLBody
89
- $idx = $html.IndexOf($keyWord)
90
- if ($idx -ge 0) { $idx += $keyWord.Length } else {
91
- $m = [regex]::Match($html, '<body[^>]*>')
92
- $idx = if ($m.Success) { $m.Index + $m.Length } else { 0 }
81
+ $insertHtml = [IO.File]::ReadAllText(${(0, run_1.psString)(insert.path)}, [Text.Encoding]::UTF8)
82
+ $anchor = ${(0, run_1.psString)(replyBody_1.WORD_SECTION_ANCHOR)}
83
+ # Read once: every HTMLBody read marshals the whole generated reply across COM,
84
+ # and a reply on a long thread runs to hundreds of KB.
85
+ $html = [string]$reply.HTMLBody
86
+ $at = $html.IndexOf($anchor)
87
+ if ($at -ge 0) { $at += $anchor.Length } else {
88
+ $bodyTag = [regex]::Match($html, '<body[^>]*>')
89
+ $at = if ($bodyTag.Success) { $bodyTag.Index + $bodyTag.Length } else { 0 }
93
90
  }
94
- $reply.HTMLBody = $html.Insert($idx, $insertHtml)
91
+ $reply.HTMLBody = $html.Insert($at, $insertHtml)
95
92
  $to = [string]$reply.To
96
93
  $subject = [string]$reply.Subject
97
- $sender = ''
98
- try { $sender = [string]$item.SenderEmailAddress } catch {}
99
- ${composeAction('reply', params.sendImmediately, params.openDraftWindow)}
100
- ConvertTo-Json @{ to = $to; subject = $subject; repliedToSender = $sender }
101
- `;
102
- try {
103
- const parsed = (0, json_1.parseObject)(await (0, run_1.runPowerShell)(script, 60000));
104
- return {
105
- to: (0, json_1.str)(parsed.to),
106
- subject: (0, json_1.str)(parsed.subject),
107
- repliedToSender: (0, json_1.str)(parsed.repliedToSender),
108
- };
94
+ ${scripts_1.SENDER_SMTP_PS}
95
+ ${dispose('reply', request.disposition)}
96
+ ConvertTo-Json -Compress @{ to = $to; subject = $subject; repliedToSender = $senderSmtp }
97
+ `, 'standard');
98
+ const result = (0, json_1.record)(output);
99
+ return { to: (0, json_1.str)(result.to), subject: (0, json_1.str)(result.subject), repliedToSender: (0, json_1.str)(result.repliedToSender) };
109
100
  }
110
101
  finally {
111
- body.cleanup();
102
+ insert.cleanup();
112
103
  }
113
104
  }
114
105
  //# 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, runPowerShell, 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 } 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/** Send, display or file a new email. */\nexport async function sendOutlookEmail(request: SendRequest): Promise<void> {\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 try {\n await runPowerShell(`${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)}\n`, 'standard');\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>;