@dawnlit/outlook-bridge 2.0.0 → 2.2.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.
- package/README.md +16 -9
- package/dist/backend.d.ts +2 -2
- package/dist/backend.js.map +1 -1
- package/dist/bridge.js +3 -3
- package/dist/bridge.js.map +1 -1
- package/dist/errors.d.ts +17 -8
- package/dist/errors.js +17 -13
- package/dist/errors.js.map +1 -1
- package/dist/index.d.ts +3 -3
- package/dist/index.js +3 -2
- package/dist/index.js.map +1 -1
- package/dist/mac/accounts.d.ts +4 -0
- package/dist/mac/accounts.js +10 -0
- package/dist/mac/accounts.js.map +1 -1
- package/dist/mac/attachments.js +1 -1
- package/dist/mac/attachments.js.map +1 -1
- package/dist/mac/bounces.js +18 -31
- package/dist/mac/bounces.js.map +1 -1
- package/dist/mac/cleanup.d.ts +11 -3
- package/dist/mac/cleanup.js +30 -58
- package/dist/mac/cleanup.js.map +1 -1
- package/dist/mac/drafts.d.ts +3 -1
- package/dist/mac/drafts.js +37 -49
- package/dist/mac/drafts.js.map +1 -1
- package/dist/mac/folders.d.ts +10 -3
- package/dist/mac/folders.js +31 -23
- package/dist/mac/folders.js.map +1 -1
- package/dist/mac/profile.d.ts +12 -0
- package/dist/mac/profile.js +75 -1
- package/dist/mac/profile.js.map +1 -1
- package/dist/mac/read.d.ts +4 -0
- package/dist/mac/read.js +68 -42
- package/dist/mac/read.js.map +1 -1
- package/dist/mac/run.d.ts +19 -5
- package/dist/mac/run.js +49 -9
- package/dist/mac/run.js.map +1 -1
- package/dist/mac/scripts.d.ts +67 -14
- package/dist/mac/scripts.js +150 -31
- package/dist/mac/scripts.js.map +1 -1
- package/dist/mac/send.d.ts +5 -4
- package/dist/mac/send.js +52 -14
- package/dist/mac/send.js.map +1 -1
- package/dist/mac/templates.js +16 -22
- package/dist/mac/templates.js.map +1 -1
- package/dist/mail.d.ts +11 -3
- package/dist/mail.js +25 -6
- package/dist/mail.js.map +1 -1
- package/dist/runtime.js.map +1 -1
- package/dist/shared/args.js.map +1 -1
- package/dist/shared/attachmentMatch.d.ts +0 -6
- package/dist/shared/attachmentMatch.js +0 -1
- package/dist/shared/attachmentMatch.js.map +1 -1
- package/dist/shared/exec.js.map +1 -1
- package/dist/shared/locate.d.ts +0 -8
- package/dist/shared/locate.js +0 -1
- package/dist/shared/locate.js.map +1 -1
- package/dist/shared/replyBody.d.ts +0 -15
- package/dist/shared/replyBody.js +0 -2
- package/dist/shared/replyBody.js.map +1 -1
- package/dist/shared/templateLimits.d.ts +14 -0
- package/dist/shared/templateLimits.js +21 -0
- package/dist/shared/templateLimits.js.map +1 -0
- package/dist/templateBody.d.ts +6 -0
- package/dist/templateBody.js +8 -1
- package/dist/templateBody.js.map +1 -1
- package/dist/types.d.ts +43 -7
- package/dist/types.js.map +1 -1
- package/dist/windows/accounts.js +1 -1
- package/dist/windows/accounts.js.map +1 -1
- package/dist/windows/attachments.js +2 -2
- package/dist/windows/attachments.js.map +1 -1
- package/dist/windows/bounces.js +7 -7
- package/dist/windows/bounces.js.map +1 -1
- package/dist/windows/cleanup.d.ts +5 -1
- package/dist/windows/cleanup.js +18 -15
- package/dist/windows/cleanup.js.map +1 -1
- package/dist/windows/drafts.d.ts +4 -4
- package/dist/windows/drafts.js +22 -33
- package/dist/windows/drafts.js.map +1 -1
- package/dist/windows/folders.js +2 -2
- package/dist/windows/folders.js.map +1 -1
- package/dist/{shared → windows}/json.d.ts +11 -6
- package/dist/{shared → windows}/json.js +12 -7
- package/dist/windows/json.js.map +1 -0
- package/dist/windows/read.js +23 -16
- package/dist/windows/read.js.map +1 -1
- package/dist/windows/run.d.ts +0 -2
- package/dist/windows/run.js +1 -2
- package/dist/windows/run.js.map +1 -1
- package/dist/windows/scripts.d.ts +10 -4
- package/dist/windows/scripts.js +21 -12
- package/dist/windows/scripts.js.map +1 -1
- package/dist/windows/send.d.ts +13 -4
- package/dist/windows/send.js +24 -6
- package/dist/windows/send.js.map +1 -1
- package/dist/windows/templates.js +11 -17
- package/dist/windows/templates.js.map +1 -1
- package/package.json +2 -2
- package/dist/shared/json.js.map +0 -1
package/dist/mac/cleanup.js
CHANGED
|
@@ -6,43 +6,19 @@ exports.purgeDeletedItems = purgeDeletedItems;
|
|
|
6
6
|
const run_1 = require("./run");
|
|
7
7
|
const scripts_1 = require("./scripts");
|
|
8
8
|
const mail_1 = require("../mail");
|
|
9
|
-
const OUTCOME_STATUSES = ['deleted', 'would-delete', 'refused', 'failed'];
|
|
10
|
-
/**
|
|
11
|
-
* Walk a folder's `container` chain to the top as the list `chainNames`,
|
|
12
|
-
* nearest first. Requires `theFolder`.
|
|
13
|
-
*
|
|
14
|
-
* This is what extends the Inbox/Sent Items refusal to their SUBFOLDERS. The
|
|
15
|
-
* chain's last element is the folder directly under the account, and an account
|
|
16
|
-
* can't hold two top-level folders of one name, so comparing that name is exact.
|
|
17
|
-
*/
|
|
18
|
-
function folderChainSnippet(indent) {
|
|
19
|
-
return `${indent}set chainNames to {}
|
|
20
|
-
${indent}set cur to theFolder
|
|
21
|
-
${indent}set guard to 0
|
|
22
|
-
${indent}repeat while cur is not missing value and guard < 25
|
|
23
|
-
${indent} -- The account's root folder has a name of missing value, and coercing
|
|
24
|
-
${indent} -- that yields the STRING "missing value" — so test before coercing.
|
|
25
|
-
${indent} set rawName to missing value
|
|
26
|
-
${indent} try
|
|
27
|
-
${indent} set rawName to name of cur
|
|
28
|
-
${indent} end try
|
|
29
|
-
${indent} if rawName is missing value then exit repeat
|
|
30
|
-
${indent} set nm to rawName as string
|
|
31
|
-
${indent} if nm is "" then exit repeat
|
|
32
|
-
${indent} set end of chainNames to nm
|
|
33
|
-
${indent} set nextUp to missing value
|
|
34
|
-
${indent} try
|
|
35
|
-
${indent} set nextUp to container of cur
|
|
36
|
-
${indent} end try
|
|
37
|
-
${indent} set cur to nextUp
|
|
38
|
-
${indent} set guard to guard + 1
|
|
39
|
-
${indent}end repeat`;
|
|
40
|
-
}
|
|
41
9
|
/**
|
|
42
10
|
* Delete mail by message id from anywhere in the account, into Deleted Items.
|
|
43
|
-
*
|
|
44
|
-
*
|
|
45
|
-
*
|
|
11
|
+
* The same four things hold the line as on Windows:
|
|
12
|
+
* - An id that resolves outside this account is refused, whatever
|
|
13
|
+
* `allowProtected` says. A message id resolves against the whole
|
|
14
|
+
* application, and the Inbox/Sent check below only knows this account's
|
|
15
|
+
* folders by name, so another mailbox's mail would otherwise pass it.
|
|
16
|
+
* - Inbox and Sent Items (and their subfolders) are refused unless
|
|
17
|
+
* `allowProtected`.
|
|
18
|
+
* - `dryRun` reports without deleting.
|
|
19
|
+
* - Every outcome names the subject and folder of the item that actually
|
|
20
|
+
* resolved — the folder only once the item is known to be this account's,
|
|
21
|
+
* since the path is built from this account's address.
|
|
46
22
|
*/
|
|
47
23
|
async function deleteOutlookEmails(request) {
|
|
48
24
|
const { valid, invalid } = (0, scripts_1.partitionMessageIds)(request.entryIds);
|
|
@@ -55,21 +31,23 @@ async function deleteOutlookEmails(request) {
|
|
|
55
31
|
}));
|
|
56
32
|
if (valid.length > 0) {
|
|
57
33
|
const acct = await (0, scripts_1.resolveMacAccount)(request.account);
|
|
34
|
+
// The protected roots' names, read off this account: the roots are the
|
|
35
|
+
// ones Windows protects, from the one list both read.
|
|
36
|
+
const readProtectedNames = mail_1.PROTECTED_ROOTS
|
|
37
|
+
.map(rootId => `${(0, scripts_1.rootFolderSnippet)(acct, scripts_1.MAC_ROOT_TERMS[rootId], 'protectedFolder')}
|
|
38
|
+
set end of protectedNames to (name of protectedFolder) as string`)
|
|
39
|
+
.join('\n');
|
|
58
40
|
const raw = await (0, run_1.runOsaScript)(`tell application "Microsoft Outlook"
|
|
59
41
|
${(0, scripts_1.accountLookupSnippet)(acct)}
|
|
60
|
-
${(0, scripts_1.
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
set sentName to (name of sentFolder) as string
|
|
42
|
+
${(0, scripts_1.accountRootIdSnippet)(acct)}
|
|
43
|
+
set protectedNames to {}
|
|
44
|
+
${readProtectedNames}
|
|
64
45
|
set out to ""
|
|
65
46
|
repeat with theId in ${(0, run_1.asIdList)(valid)}
|
|
66
47
|
set subj to ""
|
|
67
48
|
set pathText to ""
|
|
68
49
|
try
|
|
69
|
-
|
|
70
|
-
try
|
|
71
|
-
set theMsg to message id theId
|
|
72
|
-
end try
|
|
50
|
+
${(0, scripts_1.messageByIdSnippet)(' ')}
|
|
73
51
|
if theMsg is missing value then error "no item with that message id in this Outlook"
|
|
74
52
|
try
|
|
75
53
|
set subj to (subject of theMsg) as string
|
|
@@ -79,10 +57,12 @@ ${(0, scripts_1.rootFolderSnippet)(acct, 'sent items', 'sentFolder')}
|
|
|
79
57
|
set theFolder to folder of theMsg
|
|
80
58
|
end try
|
|
81
59
|
if theFolder is missing value then error "item has no parent folder - refusing to delete"
|
|
82
|
-
${folderChainSnippet(' ')}
|
|
60
|
+
${(0, scripts_1.folderChainSnippet)(' ')}
|
|
61
|
+
${(0, scripts_1.ownMailGuardSnippet)(request.account, 'delete', ' ')}
|
|
83
62
|
set pathText to my sanitizeList(my reverseList(chainNames))
|
|
63
|
+
-- The chain's top is what extends the refusal to Inbox and Sent Items SUBFOLDERS.
|
|
84
64
|
set rootName to item (count of chainNames) of chainNames
|
|
85
|
-
set isProtected to (
|
|
65
|
+
set isProtected to (protectedNames contains rootName)
|
|
86
66
|
if isProtected and not ${(0, run_1.asBool)(request.allowProtected)} then
|
|
87
67
|
set out to out & ${(0, run_1.asRow)(['(theId as string)', 'subj', 'pathText', '"refused"', (0, run_1.asString)(mail_1.PROTECTED_MAIL_REASON)])}
|
|
88
68
|
else if ${(0, run_1.asBool)(request.dryRun)} then
|
|
@@ -112,18 +92,12 @@ end reverseList`, 'scan');
|
|
|
112
92
|
entryId: (0, run_1.field)(parts, 0),
|
|
113
93
|
subject: (0, run_1.field)(parts, 1).trim(),
|
|
114
94
|
folderPath: segments.length ? (0, scripts_1.macFolderPath)(request.account, segments[0], segments.slice(1)) : '',
|
|
115
|
-
status:
|
|
95
|
+
status: (0, mail_1.deleteStatus)((0, run_1.field)(parts, 3)),
|
|
116
96
|
reason: (0, run_1.field)(parts, 4),
|
|
117
97
|
});
|
|
118
98
|
}
|
|
119
99
|
}
|
|
120
|
-
return
|
|
121
|
-
dryRun: request.dryRun,
|
|
122
|
-
deleted: items.filter(i => i.status === 'deleted').length,
|
|
123
|
-
refused: items.filter(i => i.status === 'refused').length,
|
|
124
|
-
failed: items.filter(i => i.status === 'failed').length,
|
|
125
|
-
items,
|
|
126
|
-
};
|
|
100
|
+
return (0, mail_1.deleteMailResult)(request.dryRun, items);
|
|
127
101
|
}
|
|
128
102
|
/**
|
|
129
103
|
* Permanently remove items from the account's Deleted Items. The folder is
|
|
@@ -142,12 +116,13 @@ ${(0, scripts_1.rootFolderSnippet)(acct, 'deleted items', 'trashFolder')}
|
|
|
142
116
|
on error
|
|
143
117
|
set timeList to {}
|
|
144
118
|
end try
|
|
119
|
+
set hasTimes to ((count of timeList) is (count of idList))
|
|
145
120
|
set olderThanDays to ${(0, run_1.asInt)(request.olderThanDays)}
|
|
146
121
|
set cutoff to (current date) - (olderThanDays * days)
|
|
147
122
|
set out to ""
|
|
148
123
|
repeat with i from 1 to (count of idList)
|
|
149
124
|
set stamp to missing value
|
|
150
|
-
if
|
|
125
|
+
if hasTimes then set stamp to item i of timeList
|
|
151
126
|
set keepIt to false
|
|
152
127
|
if olderThanDays > 0 and stamp is not missing value and stamp is greater than cutoff then set keepIt to true
|
|
153
128
|
set out to out & ${(0, run_1.asRow)(['(item i of idList as string)', '(keepIt as string)'])}
|
|
@@ -168,10 +143,7 @@ end tell`, 'scan');
|
|
|
168
143
|
set failedCount to 0
|
|
169
144
|
repeat with theId in ${(0, run_1.asIdList)(doomed.map(row => row.id))}
|
|
170
145
|
try
|
|
171
|
-
|
|
172
|
-
try
|
|
173
|
-
set theMsg to message id theId
|
|
174
|
-
end try
|
|
146
|
+
${(0, scripts_1.messageByIdSnippet)(' ')}
|
|
175
147
|
if theMsg is missing value then error "gone"
|
|
176
148
|
permanently delete theMsg
|
|
177
149
|
set purgedCount to purgedCount + 1
|
package/dist/mac/cleanup.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cleanup.js","sourceRoot":"","sources":["../../src/mac/cleanup.ts"],"names":[],"mappings":"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","sourcesContent":["// Deleting mail, and emptying what was already deleted.\nimport {\n asBool,\n asIdList,\n asInt,\n asRow,\n asString,\n boolField,\n field,\n intField,\n runOsaScript,\n splitFields,\n splitList,\n splitRecords,\n summaryFields\n} from './run';\nimport {\n accountLookupSnippet,\n macFolderPath,\n partitionMessageIds,\n resolveMacAccount,\n rootFolderSnippet\n} from './scripts';\nimport { PROTECTED_MAIL_REASON } from '../mail';\nimport type { DeleteEmailsRequest, PurgeRequest } from '../backend';\nimport type { DeleteMailOutcome, DeleteMailResult, PurgeDeletedItemsResult } from '../types';\n\nconst OUTCOME_STATUSES: readonly DeleteMailOutcome['status'][] = ['deleted', 'would-delete', 'refused', 'failed'];\n\n/**\n * Walk a folder's `container` chain to the top as the list `chainNames`,\n * nearest first. Requires `theFolder`.\n *\n * This is what extends the Inbox/Sent Items refusal to their SUBFOLDERS. The\n * chain's last element is the folder directly under the account, and an account\n * can't hold two top-level folders of one name, so comparing that name is exact.\n */\nfunction folderChainSnippet(indent: string): string {\n return `${indent}set chainNames to {}\n${indent}set cur to theFolder\n${indent}set guard to 0\n${indent}repeat while cur is not missing value and guard < 25\n${indent} -- The account's root folder has a name of missing value, and coercing\n${indent} -- that yields the STRING \"missing value\" — so test before coercing.\n${indent} set rawName to missing value\n${indent} try\n${indent} set rawName to name of cur\n${indent} end try\n${indent} if rawName is missing value then exit repeat\n${indent} set nm to rawName as string\n${indent} if nm is \"\" then exit repeat\n${indent} set end of chainNames to nm\n${indent} set nextUp to missing value\n${indent} try\n${indent} set nextUp to container of cur\n${indent} end try\n${indent} set cur to nextUp\n${indent} set guard to guard + 1\n${indent}end repeat`;\n}\n\n/**\n * Delete mail by message id from anywhere in the account, into Deleted Items.\n * Inbox and Sent Items (and their subfolders) are refused unless\n * `allowProtected`; `dryRun` reports without deleting; every outcome names the\n * subject and folder of the item that actually resolved.\n */\nexport async function deleteOutlookEmails(request: DeleteEmailsRequest): Promise<DeleteMailResult> {\n const {valid, invalid} = partitionMessageIds(request.entryIds);\n const items: DeleteMailOutcome[] = invalid.map(bad => ({\n entryId: bad.entryId,\n subject: '',\n folderPath: '',\n status: 'failed',\n reason: bad.error,\n }));\n if (valid.length > 0) {\n const acct = await resolveMacAccount(request.account);\n const raw = await runOsaScript(`tell application \"Microsoft Outlook\"\n${accountLookupSnippet(acct)}\n${rootFolderSnippet(acct, 'inbox', 'inboxFolder')}\n${rootFolderSnippet(acct, 'sent items', 'sentFolder')}\n set inboxName to (name of inboxFolder) as string\n set sentName to (name of sentFolder) as string\n set out to \"\"\n repeat with theId in ${asIdList(valid)}\n set subj to \"\"\n set pathText to \"\"\n try\n set theMsg to missing value\n try\n set theMsg to message id theId\n end try\n if theMsg is missing value then error \"no item with that message id in this Outlook\"\n try\n set subj to (subject of theMsg) as string\n end try\n set theFolder to missing value\n try\n set theFolder to folder of theMsg\n end try\n if theFolder is missing value then error \"item has no parent folder - refusing to delete\"\n${folderChainSnippet(' ')}\n set pathText to my sanitizeList(my reverseList(chainNames))\n set rootName to item (count of chainNames) of chainNames\n set isProtected to (rootName is inboxName) or (rootName is sentName)\n if isProtected and not ${asBool(request.allowProtected)} then\n set out to out & ${asRow(['(theId as string)', 'subj', 'pathText', '\"refused\"', asString(PROTECTED_MAIL_REASON)])}\n else if ${asBool(request.dryRun)} then\n set out to out & ${asRow(['(theId as string)', 'subj', 'pathText', '\"would-delete\"', '\"\"'])}\n else\n delete theMsg\n set out to out & ${asRow(['(theId as string)', 'subj', 'pathText', '\"deleted\"', '\"\"'])}\n end if\n on error errText\n set out to out & ${asRow(['(theId as string)', 'subj', 'pathText', '\"failed\"', 'errText'])}\n end try\n end repeat\n return out\nend tell\n\non reverseList(lst)\n set out to {}\n repeat with i from (count of lst) to 1 by -1\n set end of out to item i of lst\n end repeat\n return out\nend reverseList`, 'scan');\n\n for (const record of splitRecords(raw)) {\n const parts = splitFields(record);\n const segments = splitList(field(parts, 2));\n items.push({\n entryId: field(parts, 0),\n subject: field(parts, 1).trim(),\n folderPath: segments.length ? macFolderPath(request.account, segments[0], segments.slice(1)) : '',\n status: OUTCOME_STATUSES.find(s => s === field(parts, 3)) ?? 'failed',\n reason: field(parts, 4),\n });\n }\n }\n return {\n dryRun: request.dryRun,\n deleted: items.filter(i => i.status === 'deleted').length,\n refused: items.filter(i => i.status === 'refused').length,\n failed: items.filter(i => i.status === 'failed').length,\n items,\n };\n}\n\n/**\n * Permanently remove items from the account's Deleted Items. The folder is\n * indexed first and items destroyed by id afterwards, so deleting can't shift\n * the collection out from under the walk.\n */\nexport async function purgeDeletedItems(request: PurgeRequest): Promise<PurgeDeletedItemsResult> {\n const folderPath = macFolderPath(request.account, 'Deleted Items', []);\n const acct = await resolveMacAccount(request.account);\n const index = await runOsaScript(`tell application \"Microsoft Outlook\"\n${accountLookupSnippet(acct)}\n${rootFolderSnippet(acct, 'deleted items', 'trashFolder')}\n set idList to id of every message of trashFolder\n try\n set timeList to time received of every message of trashFolder\n on error\n set timeList to {}\n end try\n set olderThanDays to ${asInt(request.olderThanDays)}\n set cutoff to (current date) - (olderThanDays * days)\n set out to \"\"\n repeat with i from 1 to (count of idList)\n set stamp to missing value\n if (count of timeList) is (count of idList) then set stamp to item i of timeList\n set keepIt to false\n if olderThanDays > 0 and stamp is not missing value and stamp is greater than cutoff then set keepIt to true\n set out to out & ${asRow(['(item i of idList as string)', '(keepIt as string)'])}\n end repeat\n return out\nend tell`, 'scan');\n\n const rows = splitRecords(index).map(record => {\n const parts = splitFields(record);\n return {id: field(parts, 0), keep: boolField(parts, 1)};\n });\n const doomed = rows.filter(row => !row.keep);\n const kept = rows.length - doomed.length;\n if (request.dryRun || doomed.length === 0) {\n return {folderPath, dryRun: request.dryRun, matched: doomed.length, purged: 0, kept, failed: 0};\n }\n\n const summary = summaryFields(await runOsaScript(`tell application \"Microsoft Outlook\"\n set purgedCount to 0\n set failedCount to 0\n repeat with theId in ${asIdList(doomed.map(row => row.id))}\n try\n set theMsg to missing value\n try\n set theMsg to message id theId\n end try\n if theMsg is missing value then error \"gone\"\n permanently delete theMsg\n set purgedCount to purgedCount + 1\n on error\n set failedCount to failedCount + 1\n end try\n end repeat\n return ${asRow(['(purgedCount as string)', '(failedCount as string)'])}\nend tell`, 'purge'));\n return {\n folderPath,\n dryRun: request.dryRun,\n matched: doomed.length,\n purged: intField(summary, 0),\n kept,\n failed: intField(summary, 1),\n };\n}\n"]}
|
|
1
|
+
{"version":3,"file":"cleanup.js","sourceRoot":"","sources":["../../src/mac/cleanup.ts"],"names":[],"mappings":";;AA8CA,kDA+EC;AAOD,8CA2DC;AA/LD,wDAAwD;AACxD,+BAce;AACf,uCAWmB;AACnB,kCAAiG;AAIjG;;;;;;;;;;;;;GAaG;AACI,KAAK,UAAU,mBAAmB,CAAC,OAA4B;IAClE,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,GAAG,IAAA,6BAAmB,EAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;IACjE,MAAM,KAAK,GAAwB,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QACnD,OAAO,EAAE,GAAG,CAAC,OAAO;QACpB,OAAO,EAAE,EAAE;QACX,UAAU,EAAE,EAAE;QACd,MAAM,EAAE,QAAQ;QAChB,MAAM,EAAE,GAAG,CAAC,KAAK;KACpB,CAAC,CAAC,CAAC;IACJ,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACnB,MAAM,IAAI,GAAG,MAAM,IAAA,2BAAiB,EAAC,OAAO,CAAC,OAAO,CAAC,CAAC;QACtD,uEAAuE;QACvE,sDAAsD;QACtD,MAAM,kBAAkB,GAAG,sBAAe;aACrC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,GAAG,IAAA,2BAAiB,EAAC,IAAI,EAAE,wBAAc,CAAC,MAAM,CAAC,EAAE,iBAAiB,CAAC;qEAC3B,CAAC;aACzD,IAAI,CAAC,IAAI,CAAC,CAAC;QAChB,MAAM,GAAG,GAAG,MAAM,IAAA,kBAAY,EAAC;EACrC,IAAA,8BAAoB,EAAC,IAAI,CAAC;EAC1B,IAAA,8BAAoB,EAAC,IAAI,CAAC;;EAE1B,kBAAkB;;2BAEO,IAAA,cAAQ,EAAC,KAAK,CAAC;;;;EAIxC,IAAA,4BAAkB,EAAC,cAAc,CAAC;;;;;;;;;;EAUlC,IAAA,4BAAkB,EAAC,cAAc,CAAC;EAClC,IAAA,6BAAmB,EAAC,OAAO,CAAC,OAAO,EAAE,QAAQ,EAAE,cAAc,CAAC;;;;;qCAK3B,IAAA,YAAM,EAAC,OAAO,CAAC,cAAc,CAAC;mCAChC,IAAA,WAAK,EAAC,CAAC,mBAAmB,EAAE,MAAM,EAAE,UAAU,EAAE,WAAW,EAAE,IAAA,cAAQ,EAAC,4BAAqB,CAAC,CAAC,CAAC;sBAC3G,IAAA,YAAM,EAAC,OAAO,CAAC,MAAM,CAAC;mCACT,IAAA,WAAK,EAAC,CAAC,mBAAmB,EAAE,MAAM,EAAE,UAAU,EAAE,gBAAgB,EAAE,IAAI,CAAC,CAAC;;;mCAGxE,IAAA,WAAK,EAAC,CAAC,mBAAmB,EAAE,MAAM,EAAE,UAAU,EAAE,WAAW,EAAE,IAAI,CAAC,CAAC;;;+BAGvE,IAAA,WAAK,EAAC,CAAC,mBAAmB,EAAE,MAAM,EAAE,UAAU,EAAE,UAAU,EAAE,SAAS,CAAC,CAAC;;;;;;;;;;;;gBAYtF,EAAE,MAAM,CAAC,CAAC;QAElB,KAAK,MAAM,MAAM,IAAI,IAAA,kBAAY,EAAC,GAAG,CAAC,EAAE,CAAC;YACrC,MAAM,KAAK,GAAG,IAAA,iBAAW,EAAC,MAAM,CAAC,CAAC;YAClC,MAAM,QAAQ,GAAG,IAAA,eAAS,EAAC,IAAA,WAAK,EAAC,KAAK,EAAE,CAAC,CAAC,CAAC,CAAC;YAC5C,KAAK,CAAC,IAAI,CAAC;gBACP,OAAO,EAAE,IAAA,WAAK,EAAC,KAAK,EAAE,CAAC,CAAC;gBACxB,OAAO,EAAE,IAAA,WAAK,EAAC,KAAK,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE;gBAC/B,UAAU,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,IAAA,uBAAa,EAAC,OAAO,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC,CAAC,EAAE,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE;gBACjG,MAAM,EAAE,IAAA,mBAAY,EAAC,IAAA,WAAK,EAAC,KAAK,EAAE,CAAC,CAAC,CAAC;gBACrC,MAAM,EAAE,IAAA,WAAK,EAAC,KAAK,EAAE,CAAC,CAAC;aAC1B,CAAC,CAAC;QACP,CAAC;IACL,CAAC;IACD,OAAO,IAAA,uBAAgB,EAAC,OAAO,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;AACnD,CAAC;AAED;;;;GAIG;AACI,KAAK,UAAU,iBAAiB,CAAC,OAAqB;IACzD,MAAM,UAAU,GAAG,IAAA,uBAAa,EAAC,OAAO,CAAC,OAAO,EAAE,eAAe,EAAE,EAAE,CAAC,CAAC;IACvE,MAAM,IAAI,GAAG,MAAM,IAAA,2BAAiB,EAAC,OAAO,CAAC,OAAO,CAAC,CAAC;IACtD,MAAM,KAAK,GAAG,MAAM,IAAA,kBAAY,EAAC;EACnC,IAAA,8BAAoB,EAAC,IAAI,CAAC;EAC1B,IAAA,2BAAiB,EAAC,IAAI,EAAE,eAAe,EAAE,aAAa,CAAC;;;;;;;;2BAQ9B,IAAA,WAAK,EAAC,OAAO,CAAC,aAAa,CAAC;;;;;;;;2BAQ5B,IAAA,WAAK,EAAC,CAAC,8BAA8B,EAAE,oBAAoB,CAAC,CAAC;;;SAG/E,EAAE,MAAM,CAAC,CAAC;IAEf,MAAM,IAAI,GAAG,IAAA,kBAAY,EAAC,KAAK,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE;QAC1C,MAAM,KAAK,GAAG,IAAA,iBAAW,EAAC,MAAM,CAAC,CAAC;QAClC,OAAO,EAAE,EAAE,EAAE,IAAA,WAAK,EAAC,KAAK,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,IAAA,eAAS,EAAC,KAAK,EAAE,CAAC,CAAC,EAAE,CAAC;IAC9D,CAAC,CAAC,CAAC;IACH,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IAC7C,MAAM,IAAI,GAAG,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC;IACzC,IAAI,OAAO,CAAC,MAAM,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACxC,OAAO,EAAE,UAAU,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,EAAE,CAAC;IACtG,CAAC;IAED,MAAM,OAAO,GAAG,IAAA,mBAAa,EAAC,MAAM,IAAA,kBAAY,EAAC;;;2BAG1B,IAAA,cAAQ,EAAC,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;;EAE5D,IAAA,4BAAkB,EAAC,cAAc,CAAC;;;;;;;;aAQvB,IAAA,WAAK,EAAC,CAAC,yBAAyB,EAAE,yBAAyB,CAAC,CAAC;SACjE,EAAE,OAAO,CAAC,CAAC,CAAC;IACjB,OAAO;QACH,UAAU;QACV,MAAM,EAAE,OAAO,CAAC,MAAM;QACtB,OAAO,EAAE,MAAM,CAAC,MAAM;QACtB,MAAM,EAAE,IAAA,cAAQ,EAAC,OAAO,EAAE,CAAC,CAAC;QAC5B,IAAI;QACJ,MAAM,EAAE,IAAA,cAAQ,EAAC,OAAO,EAAE,CAAC,CAAC;KAC/B,CAAC;AACN,CAAC","sourcesContent":["// Deleting mail, and emptying what was already deleted.\nimport {\n asBool,\n asIdList,\n asInt,\n asRow,\n asString,\n boolField,\n field,\n intField,\n runOsaScript,\n splitFields,\n splitList,\n splitRecords,\n summaryFields,\n} from './run';\nimport {\n accountLookupSnippet,\n accountRootIdSnippet,\n folderChainSnippet,\n MAC_ROOT_TERMS,\n macFolderPath,\n messageByIdSnippet,\n ownMailGuardSnippet,\n partitionMessageIds,\n resolveMacAccount,\n rootFolderSnippet,\n} from './scripts';\nimport { deleteMailResult, deleteStatus, PROTECTED_MAIL_REASON, PROTECTED_ROOTS } from '../mail';\nimport type { DeleteEmailsRequest, PurgeRequest } from '../backend';\nimport type { DeleteMailOutcome, DeleteMailResult, PurgeDeletedItemsResult } from '../types';\n\n/**\n * Delete mail by message id from anywhere in the account, into Deleted Items.\n * The same four things hold the line as on Windows:\n * - An id that resolves outside this account is refused, whatever\n * `allowProtected` says. A message id resolves against the whole\n * application, and the Inbox/Sent check below only knows this account's\n * folders by name, so another mailbox's mail would otherwise pass it.\n * - Inbox and Sent Items (and their subfolders) are refused unless\n * `allowProtected`.\n * - `dryRun` reports without deleting.\n * - Every outcome names the subject and folder of the item that actually\n * resolved — the folder only once the item is known to be this account's,\n * since the path is built from this account's address.\n */\nexport async function deleteOutlookEmails(request: DeleteEmailsRequest): Promise<DeleteMailResult> {\n const { valid, invalid } = partitionMessageIds(request.entryIds);\n const items: DeleteMailOutcome[] = invalid.map(bad => ({\n entryId: bad.entryId,\n subject: '',\n folderPath: '',\n status: 'failed',\n reason: bad.error,\n }));\n if (valid.length > 0) {\n const acct = await resolveMacAccount(request.account);\n // The protected roots' names, read off this account: the roots are the\n // ones Windows protects, from the one list both read.\n const readProtectedNames = PROTECTED_ROOTS\n .map(rootId => `${rootFolderSnippet(acct, MAC_ROOT_TERMS[rootId], 'protectedFolder')}\n set end of protectedNames to (name of protectedFolder) as string`)\n .join('\\n');\n const raw = await runOsaScript(`tell application \"Microsoft Outlook\"\n${accountLookupSnippet(acct)}\n${accountRootIdSnippet(acct)}\n set protectedNames to {}\n${readProtectedNames}\n set out to \"\"\n repeat with theId in ${asIdList(valid)}\n set subj to \"\"\n set pathText to \"\"\n try\n${messageByIdSnippet(' ')}\n if theMsg is missing value then error \"no item with that message id in this Outlook\"\n try\n set subj to (subject of theMsg) as string\n end try\n set theFolder to missing value\n try\n set theFolder to folder of theMsg\n end try\n if theFolder is missing value then error \"item has no parent folder - refusing to delete\"\n${folderChainSnippet(' ')}\n${ownMailGuardSnippet(request.account, 'delete', ' ')}\n set pathText to my sanitizeList(my reverseList(chainNames))\n -- The chain's top is what extends the refusal to Inbox and Sent Items SUBFOLDERS.\n set rootName to item (count of chainNames) of chainNames\n set isProtected to (protectedNames contains rootName)\n if isProtected and not ${asBool(request.allowProtected)} then\n set out to out & ${asRow(['(theId as string)', 'subj', 'pathText', '\"refused\"', asString(PROTECTED_MAIL_REASON)])}\n else if ${asBool(request.dryRun)} then\n set out to out & ${asRow(['(theId as string)', 'subj', 'pathText', '\"would-delete\"', '\"\"'])}\n else\n delete theMsg\n set out to out & ${asRow(['(theId as string)', 'subj', 'pathText', '\"deleted\"', '\"\"'])}\n end if\n on error errText\n set out to out & ${asRow(['(theId as string)', 'subj', 'pathText', '\"failed\"', 'errText'])}\n end try\n end repeat\n return out\nend tell\n\non reverseList(lst)\n set out to {}\n repeat with i from (count of lst) to 1 by -1\n set end of out to item i of lst\n end repeat\n return out\nend reverseList`, 'scan');\n\n for (const record of splitRecords(raw)) {\n const parts = splitFields(record);\n const segments = splitList(field(parts, 2));\n items.push({\n entryId: field(parts, 0),\n subject: field(parts, 1).trim(),\n folderPath: segments.length ? macFolderPath(request.account, segments[0], segments.slice(1)) : '',\n status: deleteStatus(field(parts, 3)),\n reason: field(parts, 4),\n });\n }\n }\n return deleteMailResult(request.dryRun, items);\n}\n\n/**\n * Permanently remove items from the account's Deleted Items. The folder is\n * indexed first and items destroyed by id afterwards, so deleting can't shift\n * the collection out from under the walk.\n */\nexport async function purgeDeletedItems(request: PurgeRequest): Promise<PurgeDeletedItemsResult> {\n const folderPath = macFolderPath(request.account, 'Deleted Items', []);\n const acct = await resolveMacAccount(request.account);\n const index = await runOsaScript(`tell application \"Microsoft Outlook\"\n${accountLookupSnippet(acct)}\n${rootFolderSnippet(acct, 'deleted items', 'trashFolder')}\n set idList to id of every message of trashFolder\n try\n set timeList to time received of every message of trashFolder\n on error\n set timeList to {}\n end try\n set hasTimes to ((count of timeList) is (count of idList))\n set olderThanDays to ${asInt(request.olderThanDays)}\n set cutoff to (current date) - (olderThanDays * days)\n set out to \"\"\n repeat with i from 1 to (count of idList)\n set stamp to missing value\n if hasTimes then set stamp to item i of timeList\n set keepIt to false\n if olderThanDays > 0 and stamp is not missing value and stamp is greater than cutoff then set keepIt to true\n set out to out & ${asRow(['(item i of idList as string)', '(keepIt as string)'])}\n end repeat\n return out\nend tell`, 'scan');\n\n const rows = splitRecords(index).map(record => {\n const parts = splitFields(record);\n return { id: field(parts, 0), keep: boolField(parts, 1) };\n });\n const doomed = rows.filter(row => !row.keep);\n const kept = rows.length - doomed.length;\n if (request.dryRun || doomed.length === 0) {\n return { folderPath, dryRun: request.dryRun, matched: doomed.length, purged: 0, kept, failed: 0 };\n }\n\n const summary = summaryFields(await runOsaScript(`tell application \"Microsoft Outlook\"\n set purgedCount to 0\n set failedCount to 0\n repeat with theId in ${asIdList(doomed.map(row => row.id))}\n try\n${messageByIdSnippet(' ')}\n if theMsg is missing value then error \"gone\"\n permanently delete theMsg\n set purgedCount to purgedCount + 1\n on error\n set failedCount to failedCount + 1\n end try\n end repeat\n return ${asRow(['(purgedCount as string)', '(failedCount as string)'])}\nend tell`, 'purge'));\n return {\n folderPath,\n dryRun: request.dryRun,\n matched: doomed.length,\n purged: intField(summary, 0),\n kept,\n failed: intField(summary, 1),\n };\n}\n"]}
|
package/dist/mac/drafts.d.ts
CHANGED
|
@@ -12,6 +12,8 @@ export declare function deleteOutlookDrafts(request: EntryIdsRequest): Promise<D
|
|
|
12
12
|
/**
|
|
13
13
|
* Send every draft belonging to the account. The ids are snapshotted before the
|
|
14
14
|
* first send: sending moves an item out of Drafts, and mutating the collection
|
|
15
|
-
* mid-enumeration would skip every other one.
|
|
15
|
+
* mid-enumeration would skip every other one. A draft in the folder that is set
|
|
16
|
+
* to send from another account is not this account's, so it is skipped, as on
|
|
17
|
+
* Windows, rather than sent from that account.
|
|
16
18
|
*/
|
|
17
19
|
export declare function sendAllDrafts(request: AccountRequest): Promise<SendDraftsResult>;
|
package/dist/mac/drafts.js
CHANGED
|
@@ -20,13 +20,6 @@ const scripts_1 = require("./scripts");
|
|
|
20
20
|
/** The account's drafts, newest first. Bodies are previewed, never returned whole. */
|
|
21
21
|
async function listOutlookDrafts(request) {
|
|
22
22
|
const acct = await (0, scripts_1.resolveMacAccount)(request.account);
|
|
23
|
-
const preview = request.previewChars > 0
|
|
24
|
-
? ` set bodyText to ""
|
|
25
|
-
try
|
|
26
|
-
set bodyText to (plain text content of theMsg) as string
|
|
27
|
-
end try
|
|
28
|
-
if (length of bodyText) > ${(0, run_1.asInt)(request.previewChars)} then set bodyText to text 1 thru ${(0, run_1.asInt)(request.previewChars)} of bodyText`
|
|
29
|
-
: ' set bodyText to ""';
|
|
30
23
|
const raw = await (0, run_1.runOsaScript)(`tell application "Microsoft Outlook"
|
|
31
24
|
${(0, scripts_1.accountLookupSnippet)(acct)}
|
|
32
25
|
${(0, scripts_1.rootFolderSnippet)(acct, 'drafts', 'draftsFolder')}
|
|
@@ -56,7 +49,7 @@ ${(0, scripts_1.rootFolderSnippet)(acct, 'drafts', 'draftsFolder')}
|
|
|
56
49
|
end try
|
|
57
50
|
end repeat
|
|
58
51
|
end try
|
|
59
|
-
${
|
|
52
|
+
${(0, scripts_1.bodyTextSnippet)(' ', request.previewChars)}
|
|
60
53
|
set attCount to 0
|
|
61
54
|
try
|
|
62
55
|
set attCount to count of attachments of theMsg
|
|
@@ -65,7 +58,8 @@ ${preview}
|
|
|
65
58
|
try
|
|
66
59
|
set modAt to my isoDate(modification date of theMsg)
|
|
67
60
|
end try
|
|
68
|
-
|
|
61
|
+
${boundElsewhereSnippet(request.account, ' ')}
|
|
62
|
+
if not boundElsewhere then set out to out & ${(0, run_1.asRow)(['(id of theMsg as string)', 'subj', 'my joinList(toNames, ", ")', 'my sanitizeList(toAddrs)', 'bodyText', '(attCount as string)', 'modAt'])}
|
|
69
63
|
end repeat
|
|
70
64
|
return out
|
|
71
65
|
end tell`, 'standard');
|
|
@@ -92,6 +86,19 @@ end tell`, 'standard');
|
|
|
92
86
|
drafts: drafts.slice(0, request.limit),
|
|
93
87
|
};
|
|
94
88
|
}
|
|
89
|
+
/**
|
|
90
|
+
* Set `boundElsewhere` to whether `theMsg`, a message in the account's Drafts
|
|
91
|
+
* folder, is set to send from some OTHER account. An unbound draft there is the
|
|
92
|
+
* account's own. Listing, sending all and the per-id guard all apply this one
|
|
93
|
+
* rule, as Windows' Test-DraftMatches is applied to all three.
|
|
94
|
+
*/
|
|
95
|
+
function boundElsewhereSnippet(emailAccount, indent) {
|
|
96
|
+
return `${indent}set boundTo to ""
|
|
97
|
+
${indent}try
|
|
98
|
+
${indent} set boundTo to (email address of (account of theMsg)) as string
|
|
99
|
+
${indent}end try
|
|
100
|
+
${indent}set boundElsewhere to (boundTo is not "" and boundTo is not ${(0, run_1.asString)(emailAccount)})`;
|
|
101
|
+
}
|
|
95
102
|
/**
|
|
96
103
|
* The AppleScript that proves `theMsg` is one of this account's drafts before
|
|
97
104
|
* `verb` happens to it: it sits in the account's Drafts folder, and it is bound
|
|
@@ -103,11 +110,8 @@ function ownDraftGuard(emailAccount, verb) {
|
|
|
103
110
|
set inFolder to (name of (folder of theMsg)) as string
|
|
104
111
|
end try
|
|
105
112
|
if inFolder is not draftsName or draftIds does not contain (id of theMsg) then error "item is not in this account's Drafts folder - refusing to ${verb}"
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
set boundTo to (email address of (account of theMsg)) as string
|
|
109
|
-
end try
|
|
110
|
-
if boundTo is not "" and boundTo is not ${(0, run_1.asString)(emailAccount)} then error "draft is not bound to this account - refusing to ${verb}"`;
|
|
113
|
+
${boundElsewhereSnippet(emailAccount, ' ')}
|
|
114
|
+
if boundElsewhere then error "draft is not bound to this account - refusing to ${verb}"`;
|
|
111
115
|
}
|
|
112
116
|
/** Run `verb` over named drafts of the account, each proved to be its own first. */
|
|
113
117
|
async function actOnDrafts(request, verb) {
|
|
@@ -125,10 +129,7 @@ ${(0, scripts_1.rootFolderSnippet)(acct, 'drafts', 'draftsFolder')}
|
|
|
125
129
|
repeat with theId in ${(0, run_1.asIdList)(valid)}
|
|
126
130
|
set subj to ""
|
|
127
131
|
try
|
|
128
|
-
|
|
129
|
-
try
|
|
130
|
-
set theMsg to message id theId
|
|
131
|
-
end try
|
|
132
|
+
${(0, scripts_1.messageByIdSnippet)(' ')}
|
|
132
133
|
if theMsg is missing value then error "no such item in this account's Drafts folder"
|
|
133
134
|
try
|
|
134
135
|
set subj to (subject of theMsg) as string
|
|
@@ -137,22 +138,13 @@ ${ownDraftGuard(request.account, verb)}
|
|
|
137
138
|
${verb} theMsg
|
|
138
139
|
set doneCount to doneCount + 1
|
|
139
140
|
on error errText
|
|
140
|
-
set out to out & ${
|
|
141
|
+
set out to out & ${run_1.ITEM_FAILURE_ROW}
|
|
141
142
|
end try
|
|
142
143
|
end repeat
|
|
143
144
|
return ${(0, run_1.asRow)(['(doneCount as string)'])} & out
|
|
144
145
|
end tell`, 'scan');
|
|
145
|
-
const
|
|
146
|
-
return {
|
|
147
|
-
done: (0, run_1.intField)((0, run_1.splitFields)(records[0] ?? ''), 0),
|
|
148
|
-
failed: [
|
|
149
|
-
...invalid,
|
|
150
|
-
...records.slice(1).map(record => {
|
|
151
|
-
const parts = (0, run_1.splitFields)(record);
|
|
152
|
-
return { entryId: (0, run_1.field)(parts, 0), subject: (0, run_1.field)(parts, 1), error: (0, run_1.field)(parts, 2) };
|
|
153
|
-
}),
|
|
154
|
-
],
|
|
155
|
-
};
|
|
146
|
+
const { summary, failed } = (0, run_1.splitBatch)(raw);
|
|
147
|
+
return { done: (0, run_1.intField)(summary, 0), failed: [...invalid, ...failed] };
|
|
156
148
|
}
|
|
157
149
|
/** Send the named drafts. */
|
|
158
150
|
async function sendDrafts(request) {
|
|
@@ -170,7 +162,9 @@ async function deleteOutlookDrafts(request) {
|
|
|
170
162
|
/**
|
|
171
163
|
* Send every draft belonging to the account. The ids are snapshotted before the
|
|
172
164
|
* first send: sending moves an item out of Drafts, and mutating the collection
|
|
173
|
-
* mid-enumeration would skip every other one.
|
|
165
|
+
* mid-enumeration would skip every other one. A draft in the folder that is set
|
|
166
|
+
* to send from another account is not this account's, so it is skipped, as on
|
|
167
|
+
* Windows, rather than sent from that account.
|
|
174
168
|
*/
|
|
175
169
|
async function sendAllDrafts(request) {
|
|
176
170
|
const acct = await (0, scripts_1.resolveMacAccount)(request.account);
|
|
@@ -183,29 +177,23 @@ ${(0, scripts_1.rootFolderSnippet)(acct, 'drafts', 'draftsFolder')}
|
|
|
183
177
|
repeat with theId in wanted
|
|
184
178
|
set subj to ""
|
|
185
179
|
try
|
|
186
|
-
|
|
187
|
-
try
|
|
188
|
-
set theMsg to message id theId
|
|
189
|
-
end try
|
|
180
|
+
${(0, scripts_1.messageByIdSnippet)(' ')}
|
|
190
181
|
if theMsg is missing value then error "draft is no longer in the Drafts folder"
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
182
|
+
${boundElsewhereSnippet(request.account, ' ')}
|
|
183
|
+
if not boundElsewhere then
|
|
184
|
+
try
|
|
185
|
+
set subj to (subject of theMsg) as string
|
|
186
|
+
end try
|
|
187
|
+
send theMsg
|
|
188
|
+
set sentCount to sentCount + 1
|
|
189
|
+
end if
|
|
196
190
|
on error errText
|
|
197
|
-
set out to out & ${
|
|
191
|
+
set out to out & ${run_1.ITEM_FAILURE_ROW}
|
|
198
192
|
end try
|
|
199
193
|
end repeat
|
|
200
194
|
return ${(0, run_1.asRow)(['(sentCount as string)'])} & out
|
|
201
195
|
end tell`, 'scan');
|
|
202
|
-
const
|
|
203
|
-
return {
|
|
204
|
-
sent: (0, run_1.intField)((0, run_1.splitFields)(records[0] ?? ''), 0),
|
|
205
|
-
failed: records.slice(1).map(record => {
|
|
206
|
-
const parts = (0, run_1.splitFields)(record);
|
|
207
|
-
return { entryId: (0, run_1.field)(parts, 0), subject: (0, run_1.field)(parts, 1), error: (0, run_1.field)(parts, 2) };
|
|
208
|
-
}),
|
|
209
|
-
};
|
|
196
|
+
const { summary, failed } = (0, run_1.splitBatch)(raw);
|
|
197
|
+
return { sent: (0, run_1.intField)(summary, 0), failed };
|
|
210
198
|
}
|
|
211
199
|
//# sourceMappingURL=drafts.js.map
|
package/dist/mac/drafts.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"drafts.js","sourceRoot":"","sources":["../../src/mac/drafts.ts"],"names":[],"mappings":"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","sourcesContent":["// Drafts: which ones belong to an account, and listing, sending or deleting them.\n//\n// Where Windows scans two Drafts folders and proves each item's sending\n// account, macOS resolves the one Drafts folder belonging to the account —\n// through the account object, or by the folder's own id for a mailbox the\n// dictionary won't name (see profile.ts) — so a draft found there belongs to\n// the account by construction.\n//\n// What the folder can't vouch for is an item's own binding, which is why the\n// guards compare against the requested address as a literal rather than asking\n// `targetAcct`: there may be no account object to ask.\nimport {\n asIdList,\n asInt,\n asRow,\n asString,\n field,\n intField,\n runOsaScript,\n splitFields,\n splitList,\n splitRecords\n} from './run';\nimport {\n accountLookupSnippet,\n macFolderPath,\n partitionMessageIds,\n resolveMacAccount,\n rootFolderSnippet\n} from './scripts';\nimport type { AccountRequest, EntryIdsRequest, ListDraftsRequest } from '../backend';\nimport type { DeleteDraftsResult, ItemFailure, ListDraftsResult, SendDraftsResult } from '../types';\n\n/** The account's drafts, newest first. Bodies are previewed, never returned whole. */\nexport async function listOutlookDrafts(request: ListDraftsRequest): Promise<ListDraftsResult> {\n const acct = await resolveMacAccount(request.account);\n const preview = request.previewChars > 0\n ? ` set bodyText to \"\"\n try\n set bodyText to (plain text content of theMsg) as string\n end try\n if (length of bodyText) > ${asInt(request.previewChars)} then set bodyText to text 1 thru ${asInt(request.previewChars)} of bodyText`\n : ' set bodyText to \"\"';\n const raw = await runOsaScript(`tell application \"Microsoft Outlook\"\n${accountLookupSnippet(acct)}\n${rootFolderSnippet(acct, 'drafts', 'draftsFolder')}\n set out to \"\"\n repeat with theMsg in (messages of draftsFolder)\n set subj to \"\"\n try\n set subj to (subject of theMsg) as string\n end try\n set toNames to {}\n set toAddrs to {}\n try\n repeat with r in (every to recipient of theMsg)\n try\n set ea to email address of r\n set oneAddr to \"\"\n set oneName to \"\"\n try\n set oneAddr to (address of ea) as string\n end try\n try\n set oneName to (name of ea) as string\n end try\n if oneName is \"\" then set oneName to oneAddr\n if oneName is not \"\" then set end of toNames to oneName\n if oneAddr is not \"\" then set end of toAddrs to oneAddr\n end try\n end repeat\n end try\n${preview}\n set attCount to 0\n try\n set attCount to count of attachments of theMsg\n end try\n set modAt to \"\"\n try\n set modAt to my isoDate(modification date of theMsg)\n end try\n set out to out & ${asRow(['(id of theMsg as string)', 'subj', 'my joinList(toNames, \", \")', 'my sanitizeList(toAddrs)', 'bodyText', '(attCount as string)', 'modAt'])}\n end repeat\n return out\nend tell`, 'standard');\n\n const folderPath = macFolderPath(request.account, 'Drafts', []);\n const drafts = splitRecords(raw).map(record => {\n const parts = splitFields(record);\n return {\n entryId: field(parts, 0),\n subject: field(parts, 1),\n to: field(parts, 2),\n toEmails: splitList(field(parts, 3)),\n bodyPreview: field(parts, 4).replace(/\\s+/g, ' ').trim(),\n hasAttachments: intField(parts, 5) > 0,\n lastModified: field(parts, 6),\n folderPath,\n };\n });\n drafts.sort((a, b) => b.lastModified.localeCompare(a.lastModified));\n return {\n account: request.account,\n foldersScanned: [folderPath],\n count: drafts.length,\n truncated: drafts.length > request.limit,\n drafts: drafts.slice(0, request.limit),\n };\n}\n\n/**\n * The AppleScript that proves `theMsg` is one of this account's drafts before\n * `verb` happens to it: it sits in the account's Drafts folder, and it is bound\n * to this account or to none.\n */\nfunction ownDraftGuard(emailAccount: string, verb: 'send' | 'delete'): string {\n return ` set inFolder to \"\"\n try\n set inFolder to (name of (folder of theMsg)) as string\n end try\n if inFolder is not draftsName or draftIds does not contain (id of theMsg) then error \"item is not in this account's Drafts folder - refusing to ${verb}\"\n set boundTo to \"\"\n try\n set boundTo to (email address of (account of theMsg)) as string\n end try\n if boundTo is not \"\" and boundTo is not ${asString(emailAccount)} then error \"draft is not bound to this account - refusing to ${verb}\"`;\n}\n\n/** Run `verb` over named drafts of the account, each proved to be its own first. */\nasync function actOnDrafts(request: EntryIdsRequest, verb: 'send' | 'delete'): Promise<{\n done: number;\n failed: ItemFailure[]\n}> {\n const {valid, invalid} = partitionMessageIds(request.entryIds);\n if (valid.length === 0) return {done: 0, failed: invalid};\n const acct = await resolveMacAccount(request.account);\n const raw = await runOsaScript(`tell application \"Microsoft Outlook\"\n${accountLookupSnippet(acct)}\n${rootFolderSnippet(acct, 'drafts', 'draftsFolder')}\n set draftsName to (name of draftsFolder) as string\n set draftIds to id of every message of draftsFolder\n set doneCount to 0\n set out to \"\"\n repeat with theId in ${asIdList(valid)}\n set subj to \"\"\n try\n set theMsg to missing value\n try\n set theMsg to message id theId\n end try\n if theMsg is missing value then error \"no such item in this account's Drafts folder\"\n try\n set subj to (subject of theMsg) as string\n end try\n${ownDraftGuard(request.account, verb)}\n ${verb} theMsg\n set doneCount to doneCount + 1\n on error errText\n set out to out & ${asRow(['(theId as string)', 'subj', 'errText'])}\n end try\n end repeat\n return ${asRow(['(doneCount as string)'])} & out\nend tell`, 'scan');\n const records = splitRecords(raw);\n return {\n done: intField(splitFields(records[0] ?? ''), 0),\n failed: [\n ...invalid,\n ...records.slice(1).map(record => {\n const parts = splitFields(record);\n return {entryId: field(parts, 0), subject: field(parts, 1), error: field(parts, 2)};\n }),\n ],\n };\n}\n\n/** Send the named drafts. */\nexport async function sendDrafts(request: EntryIdsRequest): Promise<SendDraftsResult> {\n const {done, failed} = await actOnDrafts(request, 'send');\n return {sent: done, failed};\n}\n\n/**\n * Delete the named drafts. `delete` files an item in Deleted Items, so a\n * mistaken call stays recoverable.\n */\nexport async function deleteOutlookDrafts(request: EntryIdsRequest): Promise<DeleteDraftsResult> {\n const {done, failed} = await actOnDrafts(request, 'delete');\n return {deleted: done, failed};\n}\n\n/**\n * Send every draft belonging to the account. The ids are snapshotted before the\n * first send: sending moves an item out of Drafts, and mutating the collection\n * mid-enumeration would skip every other one.\n */\nexport async function sendAllDrafts(request: AccountRequest): Promise<SendDraftsResult> {\n const acct = await resolveMacAccount(request.account);\n const raw = await runOsaScript(`tell application \"Microsoft Outlook\"\n${accountLookupSnippet(acct)}\n${rootFolderSnippet(acct, 'drafts', 'draftsFolder')}\n set wanted to id of every message of draftsFolder\n set sentCount to 0\n set out to \"\"\n repeat with theId in wanted\n set subj to \"\"\n try\n set theMsg to missing value\n try\n set theMsg to message id theId\n end try\n if theMsg is missing value then error \"draft is no longer in the Drafts folder\"\n try\n set subj to (subject of theMsg) as string\n end try\n send theMsg\n set sentCount to sentCount + 1\n on error errText\n set out to out & ${asRow(['(theId as string)', 'subj', 'errText'])}\n end try\n end repeat\n return ${asRow(['(sentCount as string)'])} & out\nend tell`, 'scan');\n const records = splitRecords(raw);\n return {\n sent: intField(splitFields(records[0] ?? ''), 0),\n failed: records.slice(1).map(record => {\n const parts = splitFields(record);\n return {entryId: field(parts, 0), subject: field(parts, 1), error: field(parts, 2)};\n }),\n };\n}\n"]}
|
|
1
|
+
{"version":3,"file":"drafts.js","sourceRoot":"","sources":["../../src/mac/drafts.ts"],"names":[],"mappings":"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","sourcesContent":["// Drafts: which ones belong to an account, and listing, sending or deleting them.\n//\n// Where Windows scans two Drafts folders and proves each item's sending\n// account, macOS resolves the one Drafts folder belonging to the account —\n// through the account object, or by the folder's own id for a mailbox the\n// dictionary won't name (see profile.ts) — so a draft found there belongs to\n// the account by construction.\n//\n// What the folder can't vouch for is an item's own binding, which is why the\n// guards compare against the requested address as a literal rather than asking\n// `targetAcct`: there may be no account object to ask.\nimport {\n asIdList,\n asRow,\n asString,\n field,\n intField,\n ITEM_FAILURE_ROW,\n runOsaScript,\n splitBatch,\n splitFields,\n splitList,\n splitRecords,\n} from './run';\nimport {\n accountLookupSnippet,\n bodyTextSnippet,\n macFolderPath,\n messageByIdSnippet,\n partitionMessageIds,\n resolveMacAccount,\n rootFolderSnippet,\n} from './scripts';\nimport type { AccountRequest, EntryIdsRequest, ListDraftsRequest } from '../backend';\nimport type { DeleteDraftsResult, ItemFailure, ListDraftsResult, SendDraftsResult } from '../types';\n\n/** The account's drafts, newest first. Bodies are previewed, never returned whole. */\nexport async function listOutlookDrafts(request: ListDraftsRequest): Promise<ListDraftsResult> {\n const acct = await resolveMacAccount(request.account);\n const raw = await runOsaScript(`tell application \"Microsoft Outlook\"\n${accountLookupSnippet(acct)}\n${rootFolderSnippet(acct, 'drafts', 'draftsFolder')}\n set out to \"\"\n repeat with theMsg in (messages of draftsFolder)\n set subj to \"\"\n try\n set subj to (subject of theMsg) as string\n end try\n set toNames to {}\n set toAddrs to {}\n try\n repeat with r in (every to recipient of theMsg)\n try\n set ea to email address of r\n set oneAddr to \"\"\n set oneName to \"\"\n try\n set oneAddr to (address of ea) as string\n end try\n try\n set oneName to (name of ea) as string\n end try\n if oneName is \"\" then set oneName to oneAddr\n if oneName is not \"\" then set end of toNames to oneName\n if oneAddr is not \"\" then set end of toAddrs to oneAddr\n end try\n end repeat\n end try\n${bodyTextSnippet(' ', request.previewChars)}\n set attCount to 0\n try\n set attCount to count of attachments of theMsg\n end try\n set modAt to \"\"\n try\n set modAt to my isoDate(modification date of theMsg)\n end try\n${boundElsewhereSnippet(request.account, ' ')}\n if not boundElsewhere then set out to out & ${asRow(['(id of theMsg as string)', 'subj', 'my joinList(toNames, \", \")', 'my sanitizeList(toAddrs)', 'bodyText', '(attCount as string)', 'modAt'])}\n end repeat\n return out\nend tell`, 'standard');\n\n const folderPath = macFolderPath(request.account, 'Drafts', []);\n const drafts = splitRecords(raw).map(record => {\n const parts = splitFields(record);\n return {\n entryId: field(parts, 0),\n subject: field(parts, 1),\n to: field(parts, 2),\n toEmails: splitList(field(parts, 3)),\n bodyPreview: field(parts, 4).replace(/\\s+/g, ' ').trim(),\n hasAttachments: intField(parts, 5) > 0,\n lastModified: field(parts, 6),\n folderPath,\n };\n });\n drafts.sort((a, b) => b.lastModified.localeCompare(a.lastModified));\n return {\n account: request.account,\n foldersScanned: [folderPath],\n count: drafts.length,\n truncated: drafts.length > request.limit,\n drafts: drafts.slice(0, request.limit),\n };\n}\n\n/**\n * Set `boundElsewhere` to whether `theMsg`, a message in the account's Drafts\n * folder, is set to send from some OTHER account. An unbound draft there is the\n * account's own. Listing, sending all and the per-id guard all apply this one\n * rule, as Windows' Test-DraftMatches is applied to all three.\n */\nfunction boundElsewhereSnippet(emailAccount: string, indent: string): string {\n return `${indent}set boundTo to \"\"\n${indent}try\n${indent} set boundTo to (email address of (account of theMsg)) as string\n${indent}end try\n${indent}set boundElsewhere to (boundTo is not \"\" and boundTo is not ${asString(emailAccount)})`;\n}\n\n/**\n * The AppleScript that proves `theMsg` is one of this account's drafts before\n * `verb` happens to it: it sits in the account's Drafts folder, and it is bound\n * to this account or to none.\n */\nfunction ownDraftGuard(emailAccount: string, verb: 'send' | 'delete'): string {\n return ` set inFolder to \"\"\n try\n set inFolder to (name of (folder of theMsg)) as string\n end try\n if inFolder is not draftsName or draftIds does not contain (id of theMsg) then error \"item is not in this account's Drafts folder - refusing to ${verb}\"\n${boundElsewhereSnippet(emailAccount, ' ')}\n if boundElsewhere then error \"draft is not bound to this account - refusing to ${verb}\"`;\n}\n\n/** Run `verb` over named drafts of the account, each proved to be its own first. */\nasync function actOnDrafts(\n request: EntryIdsRequest,\n verb: 'send' | 'delete',\n): Promise<{ done: number; failed: ItemFailure[] }> {\n const { valid, invalid } = partitionMessageIds(request.entryIds);\n if (valid.length === 0) return { done: 0, failed: invalid };\n const acct = await resolveMacAccount(request.account);\n const raw = await runOsaScript(`tell application \"Microsoft Outlook\"\n${accountLookupSnippet(acct)}\n${rootFolderSnippet(acct, 'drafts', 'draftsFolder')}\n set draftsName to (name of draftsFolder) as string\n set draftIds to id of every message of draftsFolder\n set doneCount to 0\n set out to \"\"\n repeat with theId in ${asIdList(valid)}\n set subj to \"\"\n try\n${messageByIdSnippet(' ')}\n if theMsg is missing value then error \"no such item in this account's Drafts folder\"\n try\n set subj to (subject of theMsg) as string\n end try\n${ownDraftGuard(request.account, verb)}\n ${verb} theMsg\n set doneCount to doneCount + 1\n on error errText\n set out to out & ${ITEM_FAILURE_ROW}\n end try\n end repeat\n return ${asRow(['(doneCount as string)'])} & out\nend tell`, 'scan');\n const { summary, failed } = splitBatch(raw);\n return { done: intField(summary, 0), failed: [...invalid, ...failed] };\n}\n\n/** Send the named drafts. */\nexport async function sendDrafts(request: EntryIdsRequest): Promise<SendDraftsResult> {\n const { done, failed } = await actOnDrafts(request, 'send');\n return { sent: done, failed };\n}\n\n/**\n * Delete the named drafts. `delete` files an item in Deleted Items, so a\n * mistaken call stays recoverable.\n */\nexport async function deleteOutlookDrafts(request: EntryIdsRequest): Promise<DeleteDraftsResult> {\n const { done, failed } = await actOnDrafts(request, 'delete');\n return { deleted: done, failed };\n}\n\n/**\n * Send every draft belonging to the account. The ids are snapshotted before the\n * first send: sending moves an item out of Drafts, and mutating the collection\n * mid-enumeration would skip every other one. A draft in the folder that is set\n * to send from another account is not this account's, so it is skipped, as on\n * Windows, rather than sent from that account.\n */\nexport async function sendAllDrafts(request: AccountRequest): Promise<SendDraftsResult> {\n const acct = await resolveMacAccount(request.account);\n const raw = await runOsaScript(`tell application \"Microsoft Outlook\"\n${accountLookupSnippet(acct)}\n${rootFolderSnippet(acct, 'drafts', 'draftsFolder')}\n set wanted to id of every message of draftsFolder\n set sentCount to 0\n set out to \"\"\n repeat with theId in wanted\n set subj to \"\"\n try\n${messageByIdSnippet(' ')}\n if theMsg is missing value then error \"draft is no longer in the Drafts folder\"\n${boundElsewhereSnippet(request.account, ' ')}\n if not boundElsewhere then\n try\n set subj to (subject of theMsg) as string\n end try\n send theMsg\n set sentCount to sentCount + 1\n end if\n on error errText\n set out to out & ${ITEM_FAILURE_ROW}\n end try\n end repeat\n return ${asRow(['(sentCount as string)'])} & out\nend tell`, 'scan');\n const { summary, failed } = splitBatch(raw);\n return { sent: intField(summary, 0), failed };\n}\n"]}
|
package/dist/mac/folders.d.ts
CHANGED
|
@@ -10,8 +10,15 @@ export declare function listInboxFolders(request: ListFoldersRequest): Promise<I
|
|
|
10
10
|
* File emails into a folder of the account. Moving rewrites each message's id.
|
|
11
11
|
*
|
|
12
12
|
* Outlook for Mac's `move` reports success even where it silently does nothing,
|
|
13
|
-
* so
|
|
14
|
-
*
|
|
15
|
-
*
|
|
13
|
+
* so each move is confirmed by the destination growing, and one that didn't
|
|
14
|
+
* fails as its own item. A count compared once around the whole batch would let
|
|
15
|
+
* one real move cover for silent failures, and give no per-item results when it
|
|
16
|
+
* tripped. A message already in the destination counts as moved without being
|
|
17
|
+
* moved, as Windows' Move() to its own folder does.
|
|
18
|
+
*
|
|
19
|
+
* Only the account's own mail is moved. A message id resolves against the whole
|
|
20
|
+
* application, so each one is first shown to sit in this account — as Windows
|
|
21
|
+
* resolves every id in the account's store — and one that doesn't fails as its
|
|
22
|
+
* own item rather than being filed out of another mailbox.
|
|
16
23
|
*/
|
|
17
24
|
export declare function moveOutlookEmails(request: MoveRequest): Promise<MoveEmailsResult>;
|
package/dist/mac/folders.js
CHANGED
|
@@ -56,9 +56,16 @@ return my walkFolders(inb, 1, ${(0, run_1.asInt)(request.maxDepth)}, "")`, 'stan
|
|
|
56
56
|
* File emails into a folder of the account. Moving rewrites each message's id.
|
|
57
57
|
*
|
|
58
58
|
* Outlook for Mac's `move` reports success even where it silently does nothing,
|
|
59
|
-
* so
|
|
60
|
-
*
|
|
61
|
-
*
|
|
59
|
+
* so each move is confirmed by the destination growing, and one that didn't
|
|
60
|
+
* fails as its own item. A count compared once around the whole batch would let
|
|
61
|
+
* one real move cover for silent failures, and give no per-item results when it
|
|
62
|
+
* tripped. A message already in the destination counts as moved without being
|
|
63
|
+
* moved, as Windows' Move() to its own folder does.
|
|
64
|
+
*
|
|
65
|
+
* Only the account's own mail is moved. A message id resolves against the whole
|
|
66
|
+
* application, so each one is first shown to sit in this account — as Windows
|
|
67
|
+
* resolves every id in the account's store — and one that doesn't fails as its
|
|
68
|
+
* own item rather than being filed out of another mailbox.
|
|
62
69
|
*/
|
|
63
70
|
async function moveOutlookEmails(request) {
|
|
64
71
|
const folderPath = (0, scripts_1.macFolderPath)(request.account, request.folder.rootLabel, request.folder.segments);
|
|
@@ -68,46 +75,47 @@ async function moveOutlookEmails(request) {
|
|
|
68
75
|
const acct = await (0, scripts_1.resolveMacAccount)(request.account);
|
|
69
76
|
const raw = await (0, run_1.runOsaScript)(`tell application "Microsoft Outlook"
|
|
70
77
|
${(0, scripts_1.accountLookupSnippet)(acct)}
|
|
78
|
+
${(0, scripts_1.accountRootIdSnippet)(acct)}
|
|
71
79
|
${(0, scripts_1.mailScopeSnippet)(acct, request.folder, request.folderLabel, request.createIfMissing)}
|
|
72
|
-
set
|
|
80
|
+
set scopeId to id of scopeFolder
|
|
73
81
|
set movedCount to 0
|
|
74
82
|
set out to ""
|
|
75
83
|
repeat with theId in ${(0, run_1.asIdList)(valid)}
|
|
76
84
|
set subj to ""
|
|
77
85
|
try
|
|
78
|
-
|
|
79
|
-
try
|
|
80
|
-
set theMsg to message id theId
|
|
81
|
-
end try
|
|
86
|
+
${(0, scripts_1.messageByIdSnippet)(' ')}
|
|
82
87
|
if theMsg is missing value then error "no message with that id in this Outlook"
|
|
83
88
|
try
|
|
84
89
|
set subj to (subject of theMsg) as string
|
|
85
90
|
end try
|
|
86
|
-
|
|
91
|
+
set theFolder to missing value
|
|
92
|
+
try
|
|
93
|
+
set theFolder to folder of theMsg
|
|
94
|
+
end try
|
|
95
|
+
${(0, scripts_1.folderChainSnippet)(' ')}
|
|
96
|
+
${(0, scripts_1.ownMailGuardSnippet)(request.account, 'move', ' ')}
|
|
97
|
+
set fromId to missing value
|
|
98
|
+
try
|
|
99
|
+
set fromId to id of theFolder
|
|
100
|
+
end try
|
|
101
|
+
if fromId is not scopeId then
|
|
102
|
+
set countBefore to count of messages of scopeFolder
|
|
103
|
+
move theMsg to scopeFolder
|
|
104
|
+
if (count of messages of scopeFolder) is not greater than countBefore then error "Outlook reported the move but '" & (name of scopeFolder) & "' did not grow - not filed"
|
|
105
|
+
end if
|
|
87
106
|
set movedCount to movedCount + 1
|
|
88
107
|
on error errText
|
|
89
|
-
set out to out & ${
|
|
108
|
+
set out to out & ${run_1.ITEM_FAILURE_ROW}
|
|
90
109
|
end try
|
|
91
110
|
end repeat
|
|
92
|
-
set countAfter to count of messages of scopeFolder
|
|
93
|
-
if movedCount > 0 and countAfter is not greater than countBefore then
|
|
94
|
-
error "Outlook reported " & (movedCount as string) & " move(s) but '" & (name of scopeFolder) & "' did not grow - nothing was filed."
|
|
95
|
-
end if
|
|
96
111
|
return ${(0, run_1.asRow)(['(movedCount as string)', '(scopeCreated as string)'])} & out
|
|
97
112
|
end tell`, 'scan');
|
|
98
|
-
const
|
|
99
|
-
const summary = (0, run_1.splitFields)(records[0] ?? '');
|
|
113
|
+
const { summary, failed } = (0, run_1.splitBatch)(raw);
|
|
100
114
|
return {
|
|
101
115
|
folderPath,
|
|
102
116
|
folderCreated: (0, run_1.boolField)(summary, 1),
|
|
103
117
|
moved: (0, run_1.intField)(summary, 0),
|
|
104
|
-
failed: [
|
|
105
|
-
...invalid,
|
|
106
|
-
...records.slice(1).map(record => {
|
|
107
|
-
const parts = (0, run_1.splitFields)(record);
|
|
108
|
-
return { entryId: (0, run_1.field)(parts, 0), subject: (0, run_1.field)(parts, 1), error: (0, run_1.field)(parts, 2) };
|
|
109
|
-
}),
|
|
110
|
-
],
|
|
118
|
+
failed: [...invalid, ...failed],
|
|
111
119
|
};
|
|
112
120
|
}
|
|
113
121
|
//# sourceMappingURL=folders.js.map
|
package/dist/mac/folders.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"folders.js","sourceRoot":"","sources":["../../src/mac/folders.ts"],"names":[],"mappings":";;
|
|
1
|
+
{"version":3,"file":"folders.js","sourceRoot":"","sources":["../../src/mac/folders.ts"],"names":[],"mappings":"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","sourcesContent":["// Listing the folders under an Inbox, and filing mail into one.\nimport {\n AS_LIST_SEP,\n asIdList,\n asInt,\n asRow,\n boolField,\n field,\n intField,\n ITEM_FAILURE_ROW,\n runOsaScript,\n splitBatch,\n splitFields,\n splitList,\n splitRecords,\n} from './run';\nimport {\n accountLookupSnippet,\n accountRootIdSnippet,\n folderChainSnippet,\n macFolderPath,\n mailScopeSnippet,\n messageByIdSnippet,\n ownMailGuardSnippet,\n partitionMessageIds,\n resolveMacAccount,\n rootFolderSnippet,\n} from './scripts';\nimport type { ListFoldersRequest, MoveRequest } from '../backend';\nimport type { InboxFolderInfo, MoveEmailsResult } from '../types';\n\n/**\n * The folders under an account's Inbox with their message counts. Path segments\n * travel joined by the LIST separator and are assembled into a folder path in\n * TypeScript, rather than pushed through two layers of backslash escaping.\n */\nexport async function listInboxFolders(request: ListFoldersRequest): Promise<InboxFolderInfo[]> {\n const acct = await resolveMacAccount(request.account);\n const raw = await runOsaScript(`on walkFolders(theFolder, level, maxLevel, prefix)\n set out to \"\"\n tell application \"Microsoft Outlook\"\n set subs to mail folders of theFolder\n end tell\n repeat with f in subs\n set nm to \"\"\n set cnt to 0\n tell application \"Microsoft Outlook\"\n try\n set nm to (name of f) as string\n end try\n try\n set cnt to (count of messages of f)\n end try\n end tell\n set thisPath to prefix & nm\n set out to out & ${asRow(['nm', 'thisPath', '(cnt as string)', '(level as string)'])}\n if level < maxLevel then\n set out to out & (my walkFolders(f, level + 1, maxLevel, thisPath & ${AS_LIST_SEP}))\n end if\n end repeat\n return out\nend walkFolders\n\ntell application \"Microsoft Outlook\"\n${accountLookupSnippet(acct)}\n${rootFolderSnippet(acct, 'inbox', 'inb')}\nend tell\nreturn my walkFolders(inb, 1, ${asInt(request.maxDepth)}, \"\")`, 'standard');\n return splitRecords(raw).map(record => {\n const parts = splitFields(record);\n return {\n name: field(parts, 0),\n folderPath: macFolderPath(request.account, 'Inbox', splitList(field(parts, 1))),\n itemCount: intField(parts, 2),\n depth: intField(parts, 3, 1),\n };\n });\n}\n\n/**\n * File emails into a folder of the account. Moving rewrites each message's id.\n *\n * Outlook for Mac's `move` reports success even where it silently does nothing,\n * so each move is confirmed by the destination growing, and one that didn't\n * fails as its own item. A count compared once around the whole batch would let\n * one real move cover for silent failures, and give no per-item results when it\n * tripped. A message already in the destination counts as moved without being\n * moved, as Windows' Move() to its own folder does.\n *\n * Only the account's own mail is moved. A message id resolves against the whole\n * application, so each one is first shown to sit in this account — as Windows\n * resolves every id in the account's store — and one that doesn't fails as its\n * own item rather than being filed out of another mailbox.\n */\nexport async function moveOutlookEmails(request: MoveRequest): Promise<MoveEmailsResult> {\n const folderPath = macFolderPath(request.account, request.folder.rootLabel, request.folder.segments);\n const { valid, invalid } = partitionMessageIds(request.entryIds);\n if (valid.length === 0) return { folderPath, folderCreated: false, moved: 0, failed: invalid };\n const acct = await resolveMacAccount(request.account);\n const raw = await runOsaScript(`tell application \"Microsoft Outlook\"\n${accountLookupSnippet(acct)}\n${accountRootIdSnippet(acct)}\n${mailScopeSnippet(acct, request.folder, request.folderLabel, request.createIfMissing)}\n set scopeId to id of scopeFolder\n set movedCount to 0\n set out to \"\"\n repeat with theId in ${asIdList(valid)}\n set subj to \"\"\n try\n${messageByIdSnippet(' ')}\n if theMsg is missing value then error \"no message with that id in this Outlook\"\n try\n set subj to (subject of theMsg) as string\n end try\n set theFolder to missing value\n try\n set theFolder to folder of theMsg\n end try\n${folderChainSnippet(' ')}\n${ownMailGuardSnippet(request.account, 'move', ' ')}\n set fromId to missing value\n try\n set fromId to id of theFolder\n end try\n if fromId is not scopeId then\n set countBefore to count of messages of scopeFolder\n move theMsg to scopeFolder\n if (count of messages of scopeFolder) is not greater than countBefore then error \"Outlook reported the move but '\" & (name of scopeFolder) & \"' did not grow - not filed\"\n end if\n set movedCount to movedCount + 1\n on error errText\n set out to out & ${ITEM_FAILURE_ROW}\n end try\n end repeat\n return ${asRow(['(movedCount as string)', '(scopeCreated as string)'])} & out\nend tell`, 'scan');\n const { summary, failed } = splitBatch(raw);\n return {\n folderPath,\n folderCreated: boolField(summary, 1),\n moved: intField(summary, 0),\n failed: [...invalid, ...failed],\n };\n}\n"]}
|
package/dist/mac/profile.d.ts
CHANGED
|
@@ -4,6 +4,18 @@ export interface ProfileAccount {
|
|
|
4
4
|
/** AppleScript folder id per root term — the keys of `ROOT_SPECIAL_TYPES`. */
|
|
5
5
|
folderIds: Record<string, number>;
|
|
6
6
|
}
|
|
7
|
+
/**
|
|
8
|
+
* Why Outlook's profile can't be read, when macOS privacy control is the
|
|
9
|
+
* reason: the sentence to act on, naming the app to grant access to. null when
|
|
10
|
+
* the profile can be read, or simply isn't there — New Outlook, or no Outlook
|
|
11
|
+
* profile on this Mac.
|
|
12
|
+
*
|
|
13
|
+
* Everything else in this module stays silent on purpose, which is right for an
|
|
14
|
+
* enrichment pass and wrong when the pass was the only way to see a mailbox:
|
|
15
|
+
* the caller then hears "no accounts" or "not found" about a mailbox in plain
|
|
16
|
+
* sight in Outlook. The callers that would say that ask here first.
|
|
17
|
+
*/
|
|
18
|
+
export declare function profileRefusal(): Promise<string | null>;
|
|
7
19
|
/**
|
|
8
20
|
* Every mail account the profile knows, whether or not AppleScript can see it.
|
|
9
21
|
*
|