@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/scripts.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"scripts.js","sourceRoot":"","sources":["../../src/mac/scripts.ts"],"names":[],"mappings":"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","sourcesContent":["// The AppleScript fragments more than one macOS operation is built from.\nimport { asString } from './run';\nimport { readProfileAccounts } from './profile';\nimport { failureTag, InvalidRequestError, NotImplementedError } from '../errors';\nimport { FolderId, isOutgoingRoot } from '../mail';\nimport type { ItemFailure, MailFolderRef } from '../types';\n\n/**\n * An account as the generated AppleScript reaches it.\n *\n * `folderIds` carries what Outlook's profile database knows about the mailbox's\n * well-known folders — the only handle on a mailbox the dictionary publishes no\n * account object for (see profile.ts). It is filled in for every account the\n * profile lists, because the generated script chooses between the account\n * object and the folder id at run time; nothing here has to predict which kinds\n * of account a given Outlook build exposes.\n */\nexport interface MacAccount {\n readonly emailAccount: string;\n readonly folderIds?: Readonly<Record<string, number>>;\n}\n\n/** Look an address up in Outlook's profile database, for the folder ids it can add. */\nexport async function resolveMacAccount(emailAccount: string): Promise<MacAccount> {\n const wanted = emailAccount.trim().toLowerCase();\n const found = (await readProfileAccounts())\n .find(account => account.emailAccount.trim().toLowerCase() === wanted);\n return found ? {emailAccount, folderIds: found.folderIds} : {emailAccount};\n}\n\n/**\n * Resolve the account whose address matches into `targetAcct`, inside a\n * `tell application \"Microsoft Outlook\"` block.\n *\n * `every account` errors even in legacy mode, so the typed classes are probed.\n * A `whose email address is …` filter can't be used either: `email address` is\n * also a class name, and AppleScript resolves it as one.\n *\n * A mailbox the dictionary publishes no account object for leaves `targetAcct`\n * as `missing value` rather than failing, provided the profile gave a folder id\n * to reach it by. `needsAccountObject` is for composing mail, which can't work\n * without the object: the account a message is sent from can only be set from\n * it, and a message composed without it goes out from the default mailbox.\n */\nexport function accountLookupSnippet(acct: MacAccount, needsAccountObject = false): string {\n const probe = ` set targetAcct to missing value\n try\n repeat with a in (exchange accounts & imap accounts & pop accounts)\n if (email address of a as string) is ${asString(acct.emailAccount)} then\n set targetAcct to a\n exit repeat\n end if\n end repeat\n end try`;\n if (acct.folderIds && !needsAccountObject) return probe;\n const complaint = acct.folderIds\n ? `${failureTag('INVALID_REQUEST')}Outlook publishes no account object for '${acct.emailAccount}', `\n + 'so the account a message is sent from cannot be set. Its folders can be read; mail cannot be composed from it.'\n : `${failureTag('ACCOUNT_NOT_FOUND')}Account '${acct.emailAccount}' not found in Outlook.`;\n return `${probe}\n if targetAcct is missing value then error ${asString(complaint)}`;\n}\n\n/**\n * Bind `variable` to one of the account's well-known folders: through the\n * account object when the probe found one, else by the folder's own id. `mail\n * folder id N` resolves against the application rather than an account, and N is\n * the id Outlook's profile database records for that folder.\n */\nexport function rootFolderSnippet(acct: MacAccount, term: string, variable: string): string {\n const viaAccount = ` set ${variable} to ${term} of targetAcct`;\n const id = acct.folderIds?.[term];\n if (id !== undefined) {\n return ` if targetAcct is missing value then\n set ${variable} to mail folder id ${id}\n else\n${viaAccount}\n end if`;\n }\n // The profile listed the mailbox but not this root, so the probe is the only\n // way in — and it may have come up empty. Name the folder out of reach\n // rather than let `<term> of missing value` say it.\n if (acct.folderIds) {\n const complaint = `${failureTag('NOT_FOUND', 'folder')}Account '${acct.emailAccount}' has no ${term} folder AppleScript can reach.`;\n return ` if targetAcct is missing value then error ${asString(complaint)}\n${viaAccount}`;\n }\n return viaAccount;\n}\n\n/**\n * Outlook for Mac's dictionary term for each well-known root, by olDefaultFolders\n * id. Verified against the running app: the terms are NOT what the Windows names\n * suggest — `sent mail` and `junk email` don't compile, `sent items` and `junk\n * mail` do — and one bad term fails the whole script at compile time.\n */\nexport const MAC_ROOT_TERMS: Readonly<Record<number, string>> = {\n [FolderId.Inbox]: 'inbox',\n [FolderId.SentMail]: 'sent items',\n [FolderId.Drafts]: 'drafts',\n [FolderId.DeletedItems]: 'deleted items',\n [FolderId.Outbox]: 'outbox',\n [FolderId.Junk]: 'junk mail',\n};\n\n/** The date property a root's items carry. */\nexport function dateProperty(rootId: number): string {\n return isOutgoingRoot(rootId) ? 'time sent' : 'time received';\n}\n\n/**\n * The Windows folder-path shape (`\\\\mailbox\\Inbox\\Invoices`), assembled in\n * TypeScript: escaping backslashes through a template literal and then an\n * AppleScript literal is unreadable, and the parts are known here anyway.\n */\nexport function macFolderPath(emailAccount: string, rootLabel: string, segments: readonly string[]): string {\n return ['\\\\\\\\' + emailAccount, rootLabel, ...segments].join('\\\\');\n}\n\n/** The dictionary term for a root, or NOT_IMPLEMENTED naming what was asked. */\nexport function rootTerm(ref: MailFolderRef, folderLabel = ''): string {\n const term = MAC_ROOT_TERMS[ref.rootId];\n if (!term) {\n const asked = folderLabel ? ` (asked for '${folderLabel}')` : '';\n throw new NotImplementedError(\n `Reading the '${ref.rootLabel}' folder${asked}`,\n `macOS, whose readable roots are ${Object.values(MAC_ROOT_TERMS).join(', ')}`,\n );\n }\n return term;\n}\n\n/**\n * Resolve `scopeFolder` from `targetAcct`: a well-known root walked down through\n * any further segments. Also binds `scopeCreated`.\n *\n * Names compare with `is`, which is case-insensitive in AppleScript, matching\n * the Windows walk. A missing segment fails as NOT_FOUND rather than falling\n * back to the root; `createMissing` builds the chain instead.\n */\nexport function mailScopeSnippet(acct: MacAccount, ref: MailFolderRef, folderLabel = '', createMissing = false): string {\n const term = rootTerm(ref, folderLabel);\n const walk = ref.segments.map(segment => {\n const complaint = `${failureTag('NOT_FOUND', 'folder')}Folder '${segment}' not found under '${ref.rootLabel}'.`;\n const create = createMissing\n ? ` set foundFolder to (make new mail folder at scopeFolder with properties {name:${asString(segment)}})\n set scopeCreated to true`\n : ` error ${asString(complaint)}`;\n return `\n set foundFolder to missing value\n repeat with sf in (mail folders of scopeFolder)\n if (name of sf as string) is ${asString(segment)} then\n set foundFolder to sf\n exit repeat\n end if\n end repeat\n if foundFolder is missing value then\n${create}\n end if\n set scopeFolder to foundFolder`;\n }).join('');\n return ` set scopeCreated to false\n${rootFolderSnippet(acct, term, 'scopeFolder')}${walk}`;\n}\n\n/**\n * The message id text for a script, or INVALID_REQUEST.\n *\n * Outlook for Mac ids are small integers; a Windows EntryID is refused up front\n * rather than left to fail as a confusing \"not found\", because a caller that\n * persisted one from a Windows run needs to be told exactly that.\n */\nexport function macMessageId(entryId: string): string {\n const id = String(entryId ?? '').trim();\n if (!/^\\d+$/.test(id)) {\n throw new InvalidRequestError(\n `'${entryId}' is not an Outlook for Mac message id. Mac ids are small integers `\n + '(e.g. \"1263\") listed on this machine; a Windows EntryID cannot be resolved here.',\n );\n }\n return id;\n}\n\n/**\n * Split ids into those a script can look up and those that can only fail. A\n * batch reports an unusable id beside a genuine miss rather than throwing and\n * discarding the ids that would have worked.\n */\nexport function partitionMessageIds(entryIds: readonly string[]): { valid: string[]; invalid: ItemFailure[] } {\n const valid: string[] = [];\n const invalid: ItemFailure[] = [];\n for (const entryId of entryIds) {\n try {\n valid.push(macMessageId(entryId));\n } catch (error) {\n invalid.push({entryId: String(entryId), subject: '', error: (error as Error).message});\n }\n }\n return {valid, invalid};\n}\n\n/**\n * Resolve `variable` from a message id, or fail as NOT_FOUND. `message id N`\n * resolves against the application, so the message is found wherever it sits.\n */\nexport function messageLookupSnippet(id: string, variable = 'theMsg'): string {\n const complaint = `${failureTag('NOT_FOUND', 'email')}No email found for message id '${id}'. It may have been deleted - list the mail again for a current id.`;\n return ` set ${variable} to missing value\n try\n set ${variable} to message id ${id}\n end try\n if ${variable} is missing value then error ${asString(complaint)}`;\n}\n\n/**\n * The sender's display name and address as `sndName` / `sndAddr`. `sender` is a\n * record, and `address of sender of m` doesn't coerce, so the record is bound\n * first.\n */\nexport function senderSnippet(messageVariable = 'theMsg', indent = ' '): string {\n return `${indent}set sndName to \"\"\n${indent}set sndAddr to \"\"\n${indent}try\n${indent} set snd to sender of ${messageVariable}\n${indent} try\n${indent} set sndAddr to (address of snd) as string\n${indent} end try\n${indent} try\n${indent} set sndName to (name of snd) as string\n${indent} end try\n${indent}end try`;\n}\n\n/**\n * Everything a reader wants off one message bound to `theMsg`, emitted together\n * with the row it produces (see `messageDetailFields`) so the read order and the\n * positions the decoders index into cannot drift apart. `bodyText` is emitted\n * LAST when asked for, so a stray separator in an earlier field can't shift it.\n */\nexport function messageDetailSnippet(withBody = false, indent = ' '): string {\n return `${indent}set subj to \"\"\n${indent}try\n${indent} set subj to (subject of theMsg) as string\n${indent}end try\n${senderSnippet('theMsg', indent)}\n${indent}set recvd to \"\"\n${indent}try\n${indent} set recvd to my isoDate(time received of theMsg)\n${indent}on error\n${indent} try\n${indent} set recvd to my isoDate(time sent of theMsg)\n${indent} end try\n${indent}end try\n${indent}set attNames to {}\n${indent}try\n${indent} set attNames to name of every attachment of theMsg\n${indent}end try` + (withBody ? `\n${indent}set bodyText to \"\"\n${indent}try\n${indent} set bodyText to (plain text content of theMsg) as string\n${indent}end try` : '');\n}\n\n/** The fields `messageDetailSnippet` fills, in row order — see `MessageDetail`. */\nexport function messageDetailFields(withBody = false): string[] {\n const fields = ['(id of theMsg as string)', 'subj', 'sndName', 'sndAddr', 'recvd', 'my sanitizeList(attNames)'];\n if (withBody) fields.push('bodyText');\n return fields;\n}\n\n/** Where each of those fields lands in the row. */\nexport const MessageDetail = {\n id: 0,\n subject: 1,\n senderName: 2,\n senderEmail: 3,\n receivedTime: 4,\n attachmentNames: 5,\n body: 6,\n} as const;\n\n/**\n * The first recipient's name and address as `sndName` / `sndAddr` — what\n * outgoing mail reports in place of a sender.\n */\nexport function firstRecipientSnippet(messageVariable = 'theMsg', indent = ' '): string {\n return `${indent}set sndName to \"\"\n${indent}set sndAddr to \"\"\n${indent}try\n${indent} set rcps to to recipients of ${messageVariable}\n${indent} if (count of rcps) > 0 then\n${indent} set ea to email address of (item 1 of rcps)\n${indent} try\n${indent} set sndAddr to (address of ea) as string\n${indent} end try\n${indent} try\n${indent} set sndName to (name of ea) as string\n${indent} end try\n${indent} end if\n${indent}end try`;\n}\n\n/** Every address a message was sent to — To, CC and BCC — as the list `addrList`. */\nexport function allRecipientsSnippet(messageVariable = 'theMsg', indent = ' '): string {\n return `${indent}set addrList to {}\n${indent}try\n${indent} repeat with r in (every recipient of ${messageVariable})\n${indent} try\n${indent} -- The record is bound before its field is read: a nested\n${indent} -- 'address of (email address of r)' doesn't coerce, and fails\n${indent} -- inside the try as an empty list rather than an error.\n${indent} set ea to email address of r\n${indent} set oneAddr to (address of ea) as string\n${indent} if oneAddr is not \"\" then set end of addrList to oneAddr\n${indent} end try\n${indent} end repeat\n${indent}end try`;\n}\n\n/**\n * Recursive, case-insensitive, depth-capped folder search from `startFolder` —\n * the macOS counterpart of the Windows Find-FolderByName.\n */\nexport const FIND_FOLDER_HANDLER = `\non findFolderByName(startFolder, wantedName, depth)\n tell application \"Microsoft Outlook\"\n set subs to mail folders of startFolder\n end tell\n repeat with f in subs\n if (name of f as string) is wantedName then return f\n end repeat\n if depth is less than or equal to 1 then return missing value\n repeat with f in subs\n set hit to my findFolderByName(f, wantedName, depth - 1)\n if hit is not missing value then return hit\n end repeat\n return missing value\nend findFolderByName\n`;\n\n/**\n * Enumerate the accounts, probing each class separately: legacy Outlook errors\n * on `every account`, and New Outlook returns empty lists for the classes.\n */\nexport const LIST_ACCOUNTS_SNIPPET = `\nset acctList to {}\ntell application \"Microsoft Outlook\"\n repeat with acctClass in {\"exchange\", \"imap\", \"pop\"}\n try\n if (acctClass as string) is \"exchange\" then\n set accts to exchange accounts\n else if (acctClass as string) is \"imap\" then\n set accts to imap accounts\n else\n set accts to pop accounts\n end if\n repeat with a in accts\n try\n set end of acctList to (email address of a as string)\n end try\n end repeat\n end try\n end repeat\nend tell\n`;\n"]}
|
|
1
|
+
{"version":3,"file":"scripts.js","sourceRoot":"","sources":["../../src/mac/scripts.ts"],"names":[],"mappings":";;;AA6BA,8CASC;AAgBD,oDAuBC;AAQD,8CAmBC;AAkBD,oCAEC;AAOD,sCAEC;AAWD,gDAsBC;AAYD,oDAOC;AAiBD,kDAYC;AAuBD,4CAuBC;AASD,oCASC;AAOD,kDAWC;AAMD,oDAOC;AAQD,gDAKC;AAOD,0CASC;AAMD,sCAYC;AAQD,oDAkBC;AAGD,kDAIC;AAiBD,sDAeC;AAGD,oDAcC;AAtbD,yEAAyE;AACzE,+BAAwC;AACxC,uCAAgE;AAChE,sCAAiF;AACjF,kCAAmD;AAwBnD,uFAAuF;AAChF,KAAK,UAAU,iBAAiB,CAAC,YAAoB;IACxD,MAAM,MAAM,GAAG,YAAY,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;IACjD,MAAM,KAAK,GAAG,CAAC,MAAM,IAAA,6BAAmB,GAAE,CAAC;SACtC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,OAAO,CAAC,YAAY,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,KAAK,MAAM,CAAC,CAAC;IAC3E,IAAI,KAAK;QAAE,OAAO,EAAE,YAAY,EAAE,SAAS,EAAE,KAAK,CAAC,SAAS,EAAE,CAAC;IAC/D,6EAA6E;IAC7E,0EAA0E;IAC1E,MAAM,OAAO,GAAG,MAAM,IAAA,wBAAc,GAAE,CAAC;IACvC,OAAO,OAAO,CAAC,CAAC,CAAC,EAAE,YAAY,EAAE,cAAc,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,YAAY,EAAE,CAAC;AAClF,CAAC;AAED;;;;;;;;;;;;;GAaG;AACH,SAAgB,oBAAoB,CAAC,IAAgB,EAAE,kBAAkB,GAAG,KAAK;IAC7E,MAAM,KAAK,GAAG;;;mDAGiC,IAAA,cAAQ,EAAC,IAAI,CAAC,YAAY,CAAC;;;;;YAKlE,CAAC;IACT,IAAI,IAAI,CAAC,SAAS,IAAI,CAAC,kBAAkB;QAAE,OAAO,KAAK,CAAC;IACxD,IAAI,SAAiB,CAAC;IACtB,IAAI,IAAI,CAAC,SAAS,EAAE,CAAC;QACjB,SAAS,GAAG,GAAG,IAAA,mBAAU,EAAC,iBAAiB,CAAC,4CAA4C,IAAI,CAAC,YAAY,KAAK;cACxG,gHAAgH,CAAC;IAC3H,CAAC;SAAM,IAAI,IAAI,CAAC,cAAc,EAAE,CAAC;QAC7B,SAAS,GAAG,GAAG,IAAA,mBAAU,EAAC,mBAAmB,CAAC,YAAY,IAAI,CAAC,YAAY,mCAAmC;cACxG,YAAY,IAAI,CAAC,cAAc,EAAE,CAAC;IAC5C,CAAC;SAAM,CAAC;QACJ,SAAS,GAAG,GAAG,IAAA,mBAAU,EAAC,mBAAmB,CAAC,YAAY,IAAI,CAAC,YAAY,yBAAyB,CAAC;IACzG,CAAC;IACD,OAAO,GAAG,KAAK;gDAC6B,IAAA,cAAQ,EAAC,SAAS,CAAC,EAAE,CAAC;AACtE,CAAC;AAED;;;;;GAKG;AACH,SAAgB,iBAAiB,CAAC,IAAgB,EAAE,IAAY,EAAE,QAAgB;IAC9E,MAAM,UAAU,GAAG,WAAW,QAAQ,OAAO,IAAI,gBAAgB,CAAC;IAClE,MAAM,EAAE,GAAG,IAAI,CAAC,SAAS,EAAE,CAAC,IAAI,CAAC,CAAC;IAClC,IAAI,EAAE,KAAK,SAAS,EAAE,CAAC;QACnB,OAAO;cACD,QAAQ,sBAAsB,EAAE;;EAE5C,UAAU;WACD,CAAC;IACR,CAAC;IACD,6EAA6E;IAC7E,uEAAuE;IACvE,oDAAoD;IACpD,IAAI,IAAI,CAAC,SAAS,EAAE,CAAC;QACjB,MAAM,SAAS,GAAG,GAAG,IAAA,mBAAU,EAAC,WAAW,EAAE,QAAQ,CAAC,YAAY,IAAI,CAAC,YAAY,YAAY,IAAI,gCAAgC,CAAC;QACpI,OAAO,iDAAiD,IAAA,cAAQ,EAAC,SAAS,CAAC;EACjF,UAAU,EAAE,CAAC;IACX,CAAC;IACD,OAAO,UAAU,CAAC;AACtB,CAAC;AAED;;;;;GAKG;AACU,QAAA,cAAc,GAAqC;IAC5D,CAAC,eAAQ,CAAC,KAAK,CAAC,EAAE,OAAO;IACzB,CAAC,eAAQ,CAAC,QAAQ,CAAC,EAAE,YAAY;IACjC,CAAC,eAAQ,CAAC,MAAM,CAAC,EAAE,QAAQ;IAC3B,CAAC,eAAQ,CAAC,YAAY,CAAC,EAAE,eAAe;IACxC,CAAC,eAAQ,CAAC,MAAM,CAAC,EAAE,QAAQ;IAC3B,CAAC,eAAQ,CAAC,IAAI,CAAC,EAAE,WAAW;CAC/B,CAAC;AAEF,8CAA8C;AAC9C,SAAgB,YAAY,CAAC,MAAc;IACvC,OAAO,IAAA,qBAAc,EAAC,MAAM,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,eAAe,CAAC;AAClE,CAAC;AAED;;;;GAIG;AACH,SAAgB,aAAa,CAAC,YAAoB,EAAE,SAAiB,EAAE,QAA2B;IAC9F,OAAO,CAAC,MAAM,GAAG,YAAY,EAAE,SAAS,EAAE,GAAG,QAAQ,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACtE,CAAC;AAED;;;;;;;;GAQG;AACH,SAAgB,kBAAkB,CAAC,MAAc;IAC7C,OAAO,GAAG,MAAM;EAClB,MAAM;EACN,MAAM;EACN,MAAM;EACN,MAAM;EACN,MAAM;EACN,MAAM;EACN,MAAM;EACN,MAAM;EACN,MAAM;EACN,MAAM;EACN,MAAM;EACN,MAAM;EACN,MAAM;EACN,MAAM;EACN,MAAM;EACN,MAAM;EACN,MAAM;EACN,MAAM;EACN,MAAM;EACN,MAAM,YAAY,CAAC;AACrB,CAAC;AAED;;;;;;;;;GASG;AACH,SAAgB,oBAAoB,CAAC,IAAgB;IACjD,MAAM,QAAQ,GAAG,IAAI,CAAC,SAAS,EAAE,CAAC,aAAa,CAAC,CAAC;IACjD,OAAO;;;YAGC,GAAG,CAAC,QAAQ,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;4DACc,IAAA,WAAK,EAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;AAC/E,CAAC;AAED;;;;;;;;;;;;;;GAcG;AACH,SAAgB,mBAAmB,CAAC,YAAoB,EAAE,IAAuB,EAAE,MAAc;IAC7F,OAAO,GAAG,MAAM;EAClB,MAAM;EACN,MAAM;EACN,MAAM;EACN,MAAM;EACN,MAAM;EACN,MAAM;EACN,MAAM,0EAA0E,IAAA,cAAQ,EAAC,YAAY,CAAC;EACtG,MAAM;EACN,MAAM;EACN,MAAM,2FAA2F,IAAI,GAAG,CAAC;AAC3G,CAAC;AAED,gFAAgF;AAChF,SAAS,QAAQ,CAAC,GAAkB,EAAE,WAAW,GAAG,EAAE;IAClD,MAAM,IAAI,GAAG,sBAAc,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;IACxC,IAAI,CAAC,IAAI,EAAE,CAAC;QACR,MAAM,KAAK,GAAG,WAAW,CAAC,CAAC,CAAC,gBAAgB,WAAW,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC;QACjE,MAAM,IAAI,4BAAmB,CACzB,gBAAgB,GAAG,CAAC,SAAS,WAAW,KAAK,EAAE,EAC/C,mCAAmC,MAAM,CAAC,MAAM,CAAC,sBAAc,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAChF,CAAC;IACN,CAAC;IACD,OAAO,IAAI,CAAC;AAChB,CAAC;AAED;;;;;;;GAOG;AACH,SAAgB,gBAAgB,CAAC,IAAgB,EAAE,GAAkB,EAAE,WAAW,GAAG,EAAE,EAAE,aAAa,GAAG,KAAK;IAC1G,MAAM,IAAI,GAAG,QAAQ,CAAC,GAAG,EAAE,WAAW,CAAC,CAAC;IACxC,MAAM,IAAI,GAAG,GAAG,CAAC,QAAQ,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE;QACpC,MAAM,SAAS,GAAG,GAAG,IAAA,mBAAU,EAAC,WAAW,EAAE,QAAQ,CAAC,WAAW,OAAO,sBAAsB,GAAG,CAAC,SAAS,IAAI,CAAC;QAChH,MAAM,MAAM,GAAG,aAAa;YACxB,CAAC,CAAC,yFAAyF,IAAA,cAAQ,EAAC,OAAO,CAAC;iCACvF;YACrB,CAAC,CAAC,iBAAiB,IAAA,cAAQ,EAAC,SAAS,CAAC,EAAE,CAAC;QAC7C,OAAO;;;uCAGwB,IAAA,cAAQ,EAAC,OAAO,CAAC;;;;;;EAMtD,MAAM;;mCAE2B,CAAC;IAChC,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACZ,OAAO;EACT,iBAAiB,CAAC,IAAI,EAAE,IAAI,EAAE,aAAa,CAAC,GAAG,IAAI,EAAE,CAAC;AACxD,CAAC;AAED;;;;;;GAMG;AACH,SAAgB,YAAY,CAAC,OAAe;IACxC,MAAM,EAAE,GAAG,MAAM,CAAC,OAAO,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;IACxC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,CAAC;QACpB,MAAM,IAAI,4BAAmB,CACzB,IAAI,OAAO,qEAAqE;cAC9E,kFAAkF,CACvF,CAAC;IACN,CAAC;IACD,OAAO,EAAE,CAAC;AACd,CAAC;AAED;;;;GAIG;AACH,SAAgB,mBAAmB,CAAC,QAA2B;IAC3D,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,MAAM,OAAO,GAAkB,EAAE,CAAC;IAClC,KAAK,MAAM,OAAO,IAAI,QAAQ,EAAE,CAAC;QAC7B,IAAI,CAAC;YACD,KAAK,CAAC,IAAI,CAAC,YAAY,CAAC,OAAO,CAAC,CAAC,CAAC;QACtC,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACb,OAAO,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,MAAM,CAAC,OAAO,CAAC,EAAE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAG,KAAe,CAAC,OAAO,EAAE,CAAC,CAAC;QAC7F,CAAC;IACL,CAAC;IACD,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC;AAC9B,CAAC;AAED;;;GAGG;AACH,SAAgB,oBAAoB,CAAC,EAAU;IAC3C,MAAM,SAAS,GAAG,GAAG,IAAA,mBAAU,EAAC,WAAW,EAAE,OAAO,CAAC,kCAAkC,EAAE,qEAAqE,CAAC;IAC/J,OAAO;;mCAEwB,EAAE;;4CAEO,IAAA,cAAQ,EAAC,SAAS,CAAC,EAAE,CAAC;AAClE,CAAC;AAED;;;;;GAKG;AACH,SAAgB,kBAAkB,CAAC,MAAc;IAC7C,OAAO,GAAG,MAAM;EAClB,MAAM;EACN,MAAM;EACN,MAAM,SAAS,CAAC;AAClB,CAAC;AAED;;;;GAIG;AACH,SAAgB,eAAe,CAAC,MAAc,EAAE,QAAiB;IAC7D,IAAI,QAAQ,KAAK,CAAC;QAAE,OAAO,GAAG,MAAM,oBAAoB,CAAC;IACzD,MAAM,IAAI,GAAG,GAAG,MAAM;EACxB,MAAM;EACN,MAAM;EACN,MAAM,SAAS,CAAC;IACd,IAAI,QAAQ,KAAK,SAAS;QAAE,OAAO,IAAI,CAAC;IACxC,OAAO,GAAG,IAAI;EAChB,MAAM,6BAA6B,IAAA,WAAK,EAAC,QAAQ,CAAC,qCAAqC,IAAA,WAAK,EAAC,QAAQ,CAAC,cAAc,CAAC;AACvH,CAAC;AAED;;;GAGG;AACH,SAAgB,aAAa,CAAC,MAAM,GAAG,MAAM;IACzC,OAAO,GAAG,MAAM;EAClB,MAAM;EACN,MAAM;EACN,MAAM;EACN,MAAM;EACN,MAAM;EACN,MAAM;EACN,MAAM;EACN,MAAM;EACN,MAAM;EACN,MAAM,SAAS,CAAC;AAClB,CAAC;AAED;;;;;GAKG;AACH,SAAgB,oBAAoB,CAAC,QAAQ,GAAG,KAAK,EAAE,MAAM,GAAG,MAAM;IAClE,OAAO,GAAG,MAAM;EAClB,MAAM;EACN,MAAM;EACN,MAAM;EACN,aAAa,CAAC,MAAM,CAAC;EACrB,MAAM;EACN,MAAM;EACN,MAAM;EACN,MAAM;EACN,MAAM;EACN,MAAM;EACN,MAAM;EACN,MAAM;EACN,MAAM;EACN,MAAM;EACN,MAAM;EACN,MAAM,SAAS,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC,KAAK,eAAe,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;AACrE,CAAC;AAED,mFAAmF;AACnF,SAAgB,mBAAmB,CAAC,QAAQ,GAAG,KAAK;IAChD,MAAM,MAAM,GAAG,CAAC,0BAA0B,EAAE,MAAM,EAAE,SAAS,EAAE,SAAS,EAAE,OAAO,EAAE,qCAAqC,CAAC,CAAC;IAC1H,IAAI,QAAQ;QAAE,MAAM,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;IACtC,OAAO,MAAM,CAAC;AAClB,CAAC;AAED,mDAAmD;AACtC,QAAA,aAAa,GAAG;IACzB,EAAE,EAAE,CAAC;IACL,OAAO,EAAE,CAAC;IACV,UAAU,EAAE,CAAC;IACb,WAAW,EAAE,CAAC;IACd,YAAY,EAAE,CAAC;IACf,eAAe,EAAE,CAAC;IAClB,IAAI,EAAE,CAAC;CACD,CAAC;AAEX;;;GAGG;AACH,SAAgB,qBAAqB,CAAC,MAAM,GAAG,MAAM;IACjD,OAAO,GAAG,MAAM;EAClB,MAAM;EACN,MAAM;EACN,MAAM;EACN,MAAM;EACN,MAAM;EACN,MAAM;EACN,MAAM;EACN,MAAM;EACN,MAAM;EACN,MAAM;EACN,MAAM;EACN,MAAM;EACN,MAAM,SAAS,CAAC;AAClB,CAAC;AAED,oFAAoF;AACpF,SAAgB,oBAAoB,CAAC,MAAM,GAAG,MAAM;IAChD,OAAO,GAAG,MAAM;EAClB,MAAM;EACN,MAAM;EACN,MAAM;EACN,MAAM;EACN,MAAM;EACN,MAAM;EACN,MAAM;EACN,MAAM;EACN,MAAM;EACN,MAAM;EACN,MAAM;EACN,MAAM,SAAS,CAAC;AAClB,CAAC;AAED;;;GAGG;AACU,QAAA,mBAAmB,GAAG;;;;;;;;;;;;;;;CAelC,CAAC;AAEF;;;GAGG;AACU,QAAA,qBAAqB,GAAG;;;;;;;;;;;;;;;;;;;;CAoBpC,CAAC","sourcesContent":["// The AppleScript fragments more than one macOS operation is built from.\nimport { asInt, asString } from './run';\nimport { profileRefusal, readProfileAccounts } from './profile';\nimport { failureTag, InvalidRequestError, NotImplementedError } from '../errors';\nimport { FolderId, isOutgoingRoot } from '../mail';\nimport type { ItemFailure, MailFolderRef } from '../types';\n\n/**\n * An account as the generated AppleScript reaches it.\n *\n * `folderIds` carries what Outlook's profile database knows about the mailbox's\n * well-known folders — the only handle on a mailbox the dictionary publishes no\n * account object for (see profile.ts). It is filled in for every account the\n * profile lists, because the generated script chooses between the account\n * object and the folder id at run time; nothing here has to predict which kinds\n * of account a given Outlook build exposes.\n */\nexport interface MacAccount {\n readonly emailAccount: string;\n readonly folderIds?: Readonly<Record<string, number>>;\n /**\n * Set when the profile has no row for the address because macOS refused it:\n * the sentence to fail with should AppleScript not find the account either,\n * since the profile is where a mailbox AppleScript can't see would have been.\n */\n readonly profileRefusal?: string;\n}\n\n/** Look an address up in Outlook's profile database, for the folder ids it can add. */\nexport async function resolveMacAccount(emailAccount: string): Promise<MacAccount> {\n const wanted = emailAccount.trim().toLowerCase();\n const found = (await readProfileAccounts())\n .find(account => account.emailAccount.trim().toLowerCase() === wanted);\n if (found) return { emailAccount, folderIds: found.folderIds };\n // Asked only for an address the profile did not give: the script still looks\n // for an account object first, so a classic account works without access.\n const refusal = await profileRefusal();\n return refusal ? { emailAccount, profileRefusal: refusal } : { emailAccount };\n}\n\n/**\n * Resolve the account whose address matches into `targetAcct`, inside a\n * `tell application \"Microsoft Outlook\"` block.\n *\n * `every account` errors even in legacy mode, so the typed classes are probed.\n * A `whose email address is …` filter can't be used either: `email address` is\n * also a class name, and AppleScript resolves it as one.\n *\n * A mailbox the dictionary publishes no account object for leaves `targetAcct`\n * as `missing value` rather than failing, provided the profile gave a folder id\n * to reach it by. `needsAccountObject` is for composing mail, which can't work\n * without the object: the account a message is sent from can only be set from\n * it, and a message composed without it goes out from the default mailbox.\n */\nexport function accountLookupSnippet(acct: MacAccount, needsAccountObject = false): string {\n const probe = ` set targetAcct to missing value\n try\n repeat with a in (exchange accounts & imap accounts & pop accounts)\n if (email address of a as string) is ${asString(acct.emailAccount)} then\n set targetAcct to a\n exit repeat\n end if\n end repeat\n end try`;\n if (acct.folderIds && !needsAccountObject) return probe;\n let complaint: string;\n if (acct.folderIds) {\n complaint = `${failureTag('INVALID_REQUEST')}Outlook publishes no account object for '${acct.emailAccount}', `\n + 'so the account a message is sent from cannot be set. Its folders can be read; mail cannot be composed from it.';\n } else if (acct.profileRefusal) {\n complaint = `${failureTag('PERMISSION_DENIED')}Account '${acct.emailAccount}' is not one Outlook's scripting `\n + `can see. ${acct.profileRefusal}`;\n } else {\n complaint = `${failureTag('ACCOUNT_NOT_FOUND')}Account '${acct.emailAccount}' not found in Outlook.`;\n }\n return `${probe}\n if targetAcct is missing value then error ${asString(complaint)}`;\n}\n\n/**\n * Bind `variable` to one of the account's well-known folders: through the\n * account object when the probe found one, else by the folder's own id. `mail\n * folder id N` resolves against the application rather than an account, and N is\n * the id Outlook's profile database records for that folder.\n */\nexport function rootFolderSnippet(acct: MacAccount, term: string, variable: string): string {\n const viaAccount = ` set ${variable} to ${term} of targetAcct`;\n const id = acct.folderIds?.[term];\n if (id !== undefined) {\n return ` if targetAcct is missing value then\n set ${variable} to mail folder id ${id}\n else\n${viaAccount}\n end if`;\n }\n // The profile listed the mailbox but not this root, so the probe is the only\n // way in — and it may have come up empty. Name the folder out of reach\n // rather than let `<term> of missing value` say it.\n if (acct.folderIds) {\n const complaint = `${failureTag('NOT_FOUND', 'folder')}Account '${acct.emailAccount}' has no ${term} folder AppleScript can reach.`;\n return ` if targetAcct is missing value then error ${asString(complaint)}\n${viaAccount}`;\n }\n return viaAccount;\n}\n\n/**\n * Outlook for Mac's dictionary term for each well-known root, by olDefaultFolders\n * id. Verified against the running app: the terms are NOT what the Windows names\n * suggest — `sent mail` and `junk email` don't compile, `sent items` and `junk\n * mail` do — and one bad term fails the whole script at compile time.\n */\nexport const MAC_ROOT_TERMS: Readonly<Record<number, string>> = {\n [FolderId.Inbox]: 'inbox',\n [FolderId.SentMail]: 'sent items',\n [FolderId.Drafts]: 'drafts',\n [FolderId.DeletedItems]: 'deleted items',\n [FolderId.Outbox]: 'outbox',\n [FolderId.Junk]: 'junk mail',\n};\n\n/** The date property a root's items carry. */\nexport function dateProperty(rootId: number): string {\n return isOutgoingRoot(rootId) ? 'time sent' : 'time received';\n}\n\n/**\n * The Windows folder-path shape (`\\\\mailbox\\Inbox\\Invoices`), assembled in\n * TypeScript: escaping backslashes through a template literal and then an\n * AppleScript literal is unreadable, and the parts are known here anyway.\n */\nexport function macFolderPath(emailAccount: string, rootLabel: string, segments: readonly string[]): string {\n return ['\\\\\\\\' + emailAccount, rootLabel, ...segments].join('\\\\');\n}\n\n/**\n * Walk a folder's `container` chain to the top as the list `chainNames`,\n * nearest first. Requires `theFolder`, and leaves `cur` on the folder the walk\n * stopped at: the account's nameless root folder, when it got that far.\n *\n * The chain's last element is the folder directly under the account, and an\n * account can't hold two top-level folders of one name, so comparing that name\n * is exact.\n */\nexport function 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 * Bind `acctRootId` to the id of the account's root folder — the folder every\n * one of its top-level folders sits in — through the account object when the\n * probe found one, else from the profile database. Requires\n * `accountLookupSnippet`.\n *\n * Never fails: an account neither names a root for leaves `acctRootId` as\n * `missing value`, which `ownMailGuardSnippet` reads as a root nothing can be\n * shown to sit under.\n */\nexport function accountRootIdSnippet(acct: MacAccount): string {\n const profiled = acct.folderIds?.['root folder'];\n return ` set acctRootId to missing value\n try\n if targetAcct is not missing value then set acctRootId to id of (root folder of targetAcct)\n end try` + (profiled === undefined ? '' : `\n if acctRootId is missing value then set acctRootId to ${asInt(profiled)}`);\n}\n\n/**\n * Fail unless `theMsg` is the account's own mail — the macOS counterpart of\n * Windows resolving an id in the account's store. `message id N` resolves\n * against the application, so an id alone says nothing about whose mailbox the\n * message is in, and a stale or wrong one can name mail in another.\n *\n * Either of two signs is enough, because neither exists for every mailbox:\n * - the message's folder chain ends at the account's root folder. Requires\n * `folderChainSnippet` (for `cur`) and `accountRootIdSnippet`.\n * - the message's own account carries the address. A mailbox the dictionary\n * publishes no account object for answers this with `missing value` (see\n * profile.ts), which is why the first sign has to be able to stand alone.\n * An item neither vouches for is refused rather than given the benefit of the\n * doubt: what follows moves or deletes it.\n */\nexport function ownMailGuardSnippet(emailAccount: string, verb: 'delete' | 'move', indent: string): string {\n return `${indent}set inAccount to false\n${indent}try\n${indent} if acctRootId is not missing value and (id of cur) is acctRootId then set inAccount to true\n${indent}end try\n${indent}if not inAccount then\n${indent} try\n${indent} set ownerAddr to email address of (account of theMsg)\n${indent} if ownerAddr is not missing value and (ownerAddr as string) is ${asString(emailAccount)} then set inAccount to true\n${indent} end try\n${indent}end if\n${indent}if not inAccount then error \"item is in another mailbox, not this account - refusing to ${verb}\"`;\n}\n\n/** The dictionary term for a root, or NOT_IMPLEMENTED naming what was asked. */\nfunction rootTerm(ref: MailFolderRef, folderLabel = ''): string {\n const term = MAC_ROOT_TERMS[ref.rootId];\n if (!term) {\n const asked = folderLabel ? ` (asked for '${folderLabel}')` : '';\n throw new NotImplementedError(\n `Reading the '${ref.rootLabel}' folder${asked}`,\n `macOS, whose readable roots are ${Object.values(MAC_ROOT_TERMS).join(', ')}`,\n );\n }\n return term;\n}\n\n/**\n * Resolve `scopeFolder` from `targetAcct`: a well-known root walked down through\n * any further segments. Also binds `scopeCreated`.\n *\n * Names compare with `is`, which is case-insensitive in AppleScript, matching\n * the Windows walk. A missing segment fails as NOT_FOUND rather than falling\n * back to the root; `createMissing` builds the chain instead.\n */\nexport function mailScopeSnippet(acct: MacAccount, ref: MailFolderRef, folderLabel = '', createMissing = false): string {\n const term = rootTerm(ref, folderLabel);\n const walk = ref.segments.map(segment => {\n const complaint = `${failureTag('NOT_FOUND', 'folder')}Folder '${segment}' not found under '${ref.rootLabel}'.`;\n const create = createMissing\n ? ` set foundFolder to (make new mail folder at scopeFolder with properties {name:${asString(segment)}})\n set scopeCreated to true`\n : ` error ${asString(complaint)}`;\n return `\n set foundFolder to missing value\n repeat with sf in (mail folders of scopeFolder)\n if (name of sf as string) is ${asString(segment)} then\n set foundFolder to sf\n exit repeat\n end if\n end repeat\n if foundFolder is missing value then\n${create}\n end if\n set scopeFolder to foundFolder`;\n }).join('');\n return ` set scopeCreated to false\n${rootFolderSnippet(acct, term, 'scopeFolder')}${walk}`;\n}\n\n/**\n * The message id text for a script, or INVALID_REQUEST.\n *\n * Outlook for Mac ids are small integers; a Windows EntryID is refused up front\n * rather than left to fail as a confusing \"not found\", because a caller that\n * persisted one from a Windows run needs to be told exactly that.\n */\nexport function macMessageId(entryId: string): string {\n const id = String(entryId ?? '').trim();\n if (!/^\\d+$/.test(id)) {\n throw new InvalidRequestError(\n `'${entryId}' is not an Outlook for Mac message id. Mac ids are small integers `\n + '(e.g. \"1263\") listed on this machine; a Windows EntryID cannot be resolved here.',\n );\n }\n return id;\n}\n\n/**\n * Split ids into those a script can look up and those that can only fail. A\n * batch reports an unusable id beside a genuine miss rather than throwing and\n * discarding the ids that would have worked.\n */\nexport function partitionMessageIds(entryIds: readonly string[]): { valid: string[]; invalid: ItemFailure[] } {\n const valid: string[] = [];\n const invalid: ItemFailure[] = [];\n for (const entryId of entryIds) {\n try {\n valid.push(macMessageId(entryId));\n } catch (error) {\n invalid.push({ entryId: String(entryId), subject: '', error: (error as Error).message });\n }\n }\n return { valid, invalid };\n}\n\n/**\n * Bind `theMsg` to a message id, or fail as NOT_FOUND. `message id N` resolves\n * against the application, so the message is found wherever it sits.\n */\nexport function messageLookupSnippet(id: string): string {\n const complaint = `${failureTag('NOT_FOUND', 'email')}No email found for message id '${id}'. It may have been deleted - list the mail again for a current id.`;\n return ` set theMsg to missing value\n try\n set theMsg to message id ${id}\n end try\n if theMsg is missing value then error ${asString(complaint)}`;\n}\n\n/**\n * Bind `theMsg` to the id a batch loop holds in `theId`, leaving it `missing\n * value` when the id no longer resolves — the per-item counterpart of\n * `messageLookupSnippet`, for a loop that skips or reports a miss and goes on\n * rather than failing the whole run.\n */\nexport function messageByIdSnippet(indent: string): string {\n return `${indent}set theMsg to missing value\n${indent}try\n${indent} set theMsg to message id theId\n${indent}end try`;\n}\n\n/**\n * `theMsg`'s plain-text body as `bodyText`: whole, cut to `maxChars`, or — with\n * a `maxChars` of 0 — not read at all, the body being the one expensive\n * property of a message.\n */\nexport function bodyTextSnippet(indent: string, maxChars?: number): string {\n if (maxChars === 0) return `${indent}set bodyText to \"\"`;\n const read = `${indent}set bodyText to \"\"\n${indent}try\n${indent} set bodyText to (plain text content of theMsg) as string\n${indent}end try`;\n if (maxChars === undefined) return read;\n return `${read}\n${indent}if (length of bodyText) > ${asInt(maxChars)} then set bodyText to text 1 thru ${asInt(maxChars)} of bodyText`;\n}\n\n/**\n * `theMsg`'s sender as `sndName` / `sndAddr`. `sender` is a record, and\n * `address of sender of m` doesn't coerce, so the record is bound first.\n */\nexport function senderSnippet(indent = ' '): string {\n return `${indent}set sndName to \"\"\n${indent}set sndAddr to \"\"\n${indent}try\n${indent} set snd to sender of theMsg\n${indent} try\n${indent} set sndAddr to (address of snd) as string\n${indent} end try\n${indent} try\n${indent} set sndName to (name of snd) as string\n${indent} end try\n${indent}end try`;\n}\n\n/**\n * Everything a reader wants off one message bound to `theMsg`, emitted together\n * with the row it produces (see `messageDetailFields`) so the read order and the\n * positions the decoders index into cannot drift apart. `bodyText` is emitted\n * LAST when asked for, so a stray separator in an earlier field can't shift it.\n */\nexport function messageDetailSnippet(withBody = false, indent = ' '): string {\n return `${indent}set subj to \"\"\n${indent}try\n${indent} set subj to (subject of theMsg) as string\n${indent}end try\n${senderSnippet(indent)}\n${indent}set recvd to \"\"\n${indent}try\n${indent} set recvd to my isoDate(time received of theMsg)\n${indent}on error\n${indent} try\n${indent} set recvd to my isoDate(time sent of theMsg)\n${indent} end try\n${indent}end try\n${indent}set attNames to {}\n${indent}try\n${indent} set attNames to name of every attachment of theMsg\n${indent}end try` + (withBody ? `\\n${bodyTextSnippet(indent)}` : '');\n}\n\n/** The fields `messageDetailSnippet` fills, in row order — see `MessageDetail`. */\nexport function messageDetailFields(withBody = false): string[] {\n const fields = ['(id of theMsg as string)', 'subj', 'sndName', 'sndAddr', 'recvd', 'my sanitizePositionalList(attNames)'];\n if (withBody) fields.push('bodyText');\n return fields;\n}\n\n/** Where each of those fields lands in the row. */\nexport const MessageDetail = {\n id: 0,\n subject: 1,\n senderName: 2,\n senderEmail: 3,\n receivedTime: 4,\n attachmentNames: 5,\n body: 6,\n} as const;\n\n/**\n * `theMsg`'s first recipient as `sndName` / `sndAddr` — what outgoing mail\n * reports in place of a sender.\n */\nexport function firstRecipientSnippet(indent = ' '): string {\n return `${indent}set sndName to \"\"\n${indent}set sndAddr to \"\"\n${indent}try\n${indent} set rcps to to recipients of theMsg\n${indent} if (count of rcps) > 0 then\n${indent} set ea to email address of (item 1 of rcps)\n${indent} try\n${indent} set sndAddr to (address of ea) as string\n${indent} end try\n${indent} try\n${indent} set sndName to (name of ea) as string\n${indent} end try\n${indent} end if\n${indent}end try`;\n}\n\n/** Every address `theMsg` was sent to — To, CC and BCC — as the list `addrList`. */\nexport function allRecipientsSnippet(indent = ' '): string {\n return `${indent}set addrList to {}\n${indent}try\n${indent} repeat with r in (every recipient of theMsg)\n${indent} try\n${indent} -- The record is bound before its field is read: a nested\n${indent} -- 'address of (email address of r)' doesn't coerce, and fails\n${indent} -- inside the try as an empty list rather than an error.\n${indent} set ea to email address of r\n${indent} set oneAddr to (address of ea) as string\n${indent} if oneAddr is not \"\" then set end of addrList to oneAddr\n${indent} end try\n${indent} end repeat\n${indent}end try`;\n}\n\n/**\n * Recursive, case-insensitive, depth-capped folder search from `startFolder` —\n * the macOS counterpart of the Windows Find-FolderByName.\n */\nexport const FIND_FOLDER_HANDLER = `\non findFolderByName(startFolder, wantedName, depth)\n tell application \"Microsoft Outlook\"\n set subs to mail folders of startFolder\n end tell\n repeat with f in subs\n if (name of f as string) is wantedName then return f\n end repeat\n if depth is less than or equal to 1 then return missing value\n repeat with f in subs\n set hit to my findFolderByName(f, wantedName, depth - 1)\n if hit is not missing value then return hit\n end repeat\n return missing value\nend findFolderByName\n`;\n\n/**\n * Enumerate the accounts, probing each class separately: legacy Outlook errors\n * on `every account`, and New Outlook returns empty lists for the classes.\n */\nexport const LIST_ACCOUNTS_SNIPPET = `\nset acctList to {}\ntell application \"Microsoft Outlook\"\n repeat with acctClass in {\"exchange\", \"imap\", \"pop\"}\n try\n if (acctClass as string) is \"exchange\" then\n set accts to exchange accounts\n else if (acctClass as string) is \"imap\" then\n set accts to imap accounts\n else\n set accts to pop accounts\n end if\n repeat with a in accts\n try\n set end of acctList to (email address of a as string)\n end try\n end repeat\n end try\n end repeat\nend tell\n`;\n"]}
|
package/dist/mac/send.d.ts
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
import type { ReplyRequest, SendRequest } from '../backend';
|
|
2
|
-
import type { ReplyEmailResult } from '../types';
|
|
3
|
-
/** Send, display or file a new email. */
|
|
4
|
-
export declare function sendOutlookEmail(request: SendRequest): Promise<
|
|
2
|
+
import type { ReplyEmailResult, SendEmailResult } from '../types';
|
|
3
|
+
/** Send, display or file a new email; a filed draft reports its id. */
|
|
4
|
+
export declare function sendOutlookEmail(request: SendRequest): Promise<SendEmailResult>;
|
|
5
5
|
/**
|
|
6
6
|
* Reply to an email with the composed HTML above the quoted original.
|
|
7
7
|
*
|
|
8
8
|
* `reply to` builds the same quoted body COM's Reply() returns, so the insertion
|
|
9
9
|
* point is the one thing the platforms must agree on (see WORD_SECTION_ANCHOR).
|
|
10
10
|
* `to` comes back as the reply's resolved addresses: Outlook for Mac exposes
|
|
11
|
-
* recipients as records, and the address is the half worth checking.
|
|
11
|
+
* recipients as records, and the address is the half worth checking. A filed
|
|
12
|
+
* draft is named by the id Drafts gained, as a new email's is.
|
|
12
13
|
*/
|
|
13
14
|
export declare function replyOutlookEmail(request: ReplyRequest): Promise<ReplyEmailResult>;
|
package/dist/mac/send.js
CHANGED
|
@@ -7,25 +7,55 @@ const run_1 = require("./run");
|
|
|
7
7
|
const scripts_1 = require("./scripts");
|
|
8
8
|
const args_1 = require("../shared/args");
|
|
9
9
|
/**
|
|
10
|
-
* What to do with a composed message.
|
|
10
|
+
* What to do with a composed message. A filed draft leaves its id in
|
|
11
|
+
* `filedDraftId` ("" for the other dispositions, or when it can't be told).
|
|
11
12
|
*
|
|
12
13
|
* A new outgoing message lands in Temporary Items, not Drafts — `open` only
|
|
13
14
|
* shows it — so a windowless draft is moved into Drafts explicitly. `move`
|
|
14
15
|
* reports success even where it silently does nothing, so the folder is
|
|
15
|
-
* confirmed to
|
|
16
|
+
* confirmed to hold a message it didn't before rather than report a draft that
|
|
17
|
+
* doesn't exist.
|
|
18
|
+
*
|
|
19
|
+
* Moving rewrites a message's id, so the draft's id is the one Drafts gained.
|
|
20
|
+
* Another draft can land in the same moment — an autosave of a compose window,
|
|
21
|
+
* a sync — and then the subject tells them apart; a tie is left unnamed rather
|
|
22
|
+
* than guessed, since a caller that keeps the id may later send by it.
|
|
16
23
|
*/
|
|
17
24
|
function dispose(variable, disposition) {
|
|
18
25
|
switch (disposition) {
|
|
19
26
|
case 'send':
|
|
20
|
-
return { prelude: '', action: ` send ${variable}` };
|
|
27
|
+
return { prelude: ' set filedDraftId to ""', action: ` send ${variable}` };
|
|
21
28
|
case 'display':
|
|
22
|
-
return { prelude: '', action: ` open ${variable}` };
|
|
29
|
+
return { prelude: ' set filedDraftId to ""', action: ` open ${variable}` };
|
|
23
30
|
case 'save':
|
|
24
31
|
return {
|
|
25
|
-
prelude: ` set
|
|
26
|
-
set
|
|
27
|
-
|
|
28
|
-
|
|
32
|
+
prelude: ` set filedDraftId to ""
|
|
33
|
+
set draftsFolder to drafts of targetAcct
|
|
34
|
+
set draftIdsBefore to id of every message of draftsFolder`,
|
|
35
|
+
action: ` set filedSubject to ""
|
|
36
|
+
try
|
|
37
|
+
set filedSubject to (subject of ${variable}) as string
|
|
38
|
+
end try
|
|
39
|
+
move ${variable} to draftsFolder
|
|
40
|
+
-- Bound before the loop, so it walks a local list rather than asking
|
|
41
|
+
-- Outlook for each item in turn.
|
|
42
|
+
set draftIdsAfter to id of every message of draftsFolder
|
|
43
|
+
set newIds to {}
|
|
44
|
+
repeat with anId in draftIdsAfter
|
|
45
|
+
if draftIdsBefore does not contain (contents of anId) then set end of newIds to (contents of anId)
|
|
46
|
+
end repeat
|
|
47
|
+
if (count of newIds) is 0 then error "Outlook did not file the draft in the Drafts folder."
|
|
48
|
+
if (count of newIds) is 1 then
|
|
49
|
+
set filedDraftId to (item 1 of newIds) as string
|
|
50
|
+
else
|
|
51
|
+
set sameSubject to {}
|
|
52
|
+
repeat with anId in newIds
|
|
53
|
+
try
|
|
54
|
+
if ((subject of message id (contents of anId)) as string) is filedSubject then set end of sameSubject to (contents of anId)
|
|
55
|
+
end try
|
|
56
|
+
end repeat
|
|
57
|
+
if (count of sameSubject) is 1 then set filedDraftId to (item 1 of sameSubject) as string
|
|
58
|
+
end if`,
|
|
29
59
|
};
|
|
30
60
|
}
|
|
31
61
|
}
|
|
@@ -39,7 +69,7 @@ function recipientLines(kind, entries) {
|
|
|
39
69
|
return ` make new ${kind} recipient at newMsg with properties {email address:${emailAddress}}`;
|
|
40
70
|
});
|
|
41
71
|
}
|
|
42
|
-
/** Send, display or file a new email. */
|
|
72
|
+
/** Send, display or file a new email; a filed draft reports its id. */
|
|
43
73
|
async function sendOutlookEmail(request) {
|
|
44
74
|
const acct = await (0, scripts_1.resolveMacAccount)(request.account);
|
|
45
75
|
const { prelude, action } = dispose('newMsg', request.disposition);
|
|
@@ -49,7 +79,7 @@ async function sendOutlookEmail(request) {
|
|
|
49
79
|
...recipientLines('bcc', request.bcc),
|
|
50
80
|
...request.attachments.map(file => ` make new attachment at newMsg with properties {file:POSIX file ${(0, run_1.asString)(file)}}`),
|
|
51
81
|
];
|
|
52
|
-
await (0, run_1.runOsaScript)(`
|
|
82
|
+
const raw = await (0, run_1.runOsaScript)(`
|
|
53
83
|
tell application "Microsoft Outlook"
|
|
54
84
|
${(0, scripts_1.accountLookupSnippet)(acct, true)}
|
|
55
85
|
${prelude}
|
|
@@ -57,7 +87,9 @@ ${prelude}
|
|
|
57
87
|
${lines.join('\n')}
|
|
58
88
|
set account of newMsg to targetAcct
|
|
59
89
|
${action}
|
|
90
|
+
return filedDraftId
|
|
60
91
|
end tell`, 'standard');
|
|
92
|
+
return { draftEntryId: raw.trim() };
|
|
61
93
|
}
|
|
62
94
|
/**
|
|
63
95
|
* Reply to an email with the composed HTML above the quoted original.
|
|
@@ -65,7 +97,8 @@ end tell`, 'standard');
|
|
|
65
97
|
* `reply to` builds the same quoted body COM's Reply() returns, so the insertion
|
|
66
98
|
* point is the one thing the platforms must agree on (see WORD_SECTION_ANCHOR).
|
|
67
99
|
* `to` comes back as the reply's resolved addresses: Outlook for Mac exposes
|
|
68
|
-
* recipients as records, and the address is the half worth checking.
|
|
100
|
+
* recipients as records, and the address is the half worth checking. A filed
|
|
101
|
+
* draft is named by the id Drafts gained, as a new email's is.
|
|
69
102
|
*/
|
|
70
103
|
async function replyOutlookEmail(request) {
|
|
71
104
|
const id = (0, scripts_1.macMessageId)(request.email.entryId);
|
|
@@ -105,9 +138,14 @@ ${prelude}
|
|
|
105
138
|
set subj to (subject of theReply) as string
|
|
106
139
|
end try
|
|
107
140
|
${action}
|
|
108
|
-
return
|
|
141
|
+
return ${(0, run_1.asRow)(['toLine', 'subj', 'repliedTo', 'filedDraftId'])}
|
|
109
142
|
end tell`, 'standard');
|
|
110
|
-
const parts = (0, run_1.
|
|
111
|
-
return {
|
|
143
|
+
const parts = (0, run_1.summaryFields)(raw);
|
|
144
|
+
return {
|
|
145
|
+
to: (0, run_1.field)(parts, 0),
|
|
146
|
+
subject: (0, run_1.field)(parts, 1),
|
|
147
|
+
repliedToSender: (0, run_1.field)(parts, 2),
|
|
148
|
+
draftEntryId: (0, run_1.field)(parts, 3),
|
|
149
|
+
};
|
|
112
150
|
}
|
|
113
151
|
//# sourceMappingURL=send.js.map
|
package/dist/mac/send.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"send.js","sourceRoot":"","sources":["../../src/mac/send.ts"],"names":[],"mappings":";;
|
|
1
|
+
{"version":3,"file":"send.js","sourceRoot":"","sources":["../../src/mac/send.ts"],"names":[],"mappings":"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","sourcesContent":["// Composing outgoing mail on macOS: a new email, and a reply to an existing one.\nimport { asRow, asString, field, runOsaScript, summaryFields } from './run';\nimport { accountLookupSnippet, macMessageId, messageLookupSnippet, resolveMacAccount } from './scripts';\nimport { parseRecipient } from '../shared/args';\nimport type { Disposition, ReplyRequest, SendRequest } from '../backend';\nimport type { ReplyEmailResult, SendEmailResult } from '../types';\n\n/**\n * What to do with a composed message. A filed draft leaves its id in\n * `filedDraftId` (\"\" for the other dispositions, or when it can't be told).\n *\n * A new outgoing message lands in Temporary Items, not Drafts — `open` only\n * shows it — so a windowless draft is moved into Drafts explicitly. `move`\n * reports success even where it silently does nothing, so the folder is\n * confirmed to hold a message it didn't before rather than report a draft that\n * doesn't exist.\n *\n * Moving rewrites a message's id, so the draft's id is the one Drafts gained.\n * Another draft can land in the same moment — an autosave of a compose window,\n * a sync — and then the subject tells them apart; a tie is left unnamed rather\n * than guessed, since a caller that keeps the id may later send by it.\n */\nfunction dispose(variable: string, disposition: Disposition): { prelude: string; action: string } {\n switch (disposition) {\n case 'send':\n return { prelude: ' set filedDraftId to \"\"', action: ` send ${variable}` };\n case 'display':\n return { prelude: ' set filedDraftId to \"\"', action: ` open ${variable}` };\n case 'save':\n return {\n prelude: ` set filedDraftId to \"\"\n set draftsFolder to drafts of targetAcct\n set draftIdsBefore to id of every message of draftsFolder`,\n action: ` set filedSubject to \"\"\n try\n set filedSubject to (subject of ${variable}) as string\n end try\n move ${variable} to draftsFolder\n -- Bound before the loop, so it walks a local list rather than asking\n -- Outlook for each item in turn.\n set draftIdsAfter to id of every message of draftsFolder\n set newIds to {}\n repeat with anId in draftIdsAfter\n if draftIdsBefore does not contain (contents of anId) then set end of newIds to (contents of anId)\n end repeat\n if (count of newIds) is 0 then error \"Outlook did not file the draft in the Drafts folder.\"\n if (count of newIds) is 1 then\n set filedDraftId to (item 1 of newIds) as string\n else\n set sameSubject to {}\n repeat with anId in newIds\n try\n if ((subject of message id (contents of anId)) as string) is filedSubject then set end of sameSubject to (contents of anId)\n end try\n end repeat\n if (count of sameSubject) is 1 then set filedDraftId to (item 1 of sameSubject) as string\n end if`,\n };\n }\n}\n\n/** One `make new … recipient` line per entry, keeping a display name when given. */\nfunction recipientLines(kind: 'to' | 'cc' | 'bcc', entries: readonly string[]): string[] {\n return entries.map(entry => {\n const { name, address } = parseRecipient(entry);\n const emailAddress = name\n ? `{name:${asString(name)}, address:${asString(address)}}`\n : `{address:${asString(address)}}`;\n return ` make new ${kind} recipient at newMsg with properties {email address:${emailAddress}}`;\n });\n}\n\n/** Send, display or file a new email; a filed draft reports its id. */\nexport async function sendOutlookEmail(request: SendRequest): Promise<SendEmailResult> {\n const acct = await resolveMacAccount(request.account);\n const { prelude, action } = dispose('newMsg', request.disposition);\n const lines = [\n ...recipientLines('to', request.to),\n ...recipientLines('cc', request.cc),\n ...recipientLines('bcc', request.bcc),\n ...request.attachments.map(file => ` make new attachment at newMsg with properties {file:POSIX file ${asString(file)}}`),\n ];\n const raw = await runOsaScript(`\ntell application \"Microsoft Outlook\"\n${accountLookupSnippet(acct, true)}\n${prelude}\n set newMsg to make new outgoing message with properties {subject:${asString(request.subject)}, content:${asString(request.htmlBody)}}\n${lines.join('\\n')}\n set account of newMsg to targetAcct\n${action}\n return filedDraftId\nend tell`, 'standard');\n return { draftEntryId: raw.trim() };\n}\n\n/**\n * Reply to an email with the composed HTML above the quoted original.\n *\n * `reply to` builds the same quoted body COM's Reply() returns, so the insertion\n * point is the one thing the platforms must agree on (see WORD_SECTION_ANCHOR).\n * `to` comes back as the reply's resolved addresses: Outlook for Mac exposes\n * recipients as records, and the address is the half worth checking. A filed\n * draft is named by the id Drafts gained, as a new email's is.\n */\nexport async function replyOutlookEmail(request: ReplyRequest): Promise<ReplyEmailResult> {\n const id = macMessageId(request.email.entryId);\n const acct = await resolveMacAccount(request.account);\n const { prelude, action } = dispose('theReply', request.disposition);\n const raw = await runOsaScript(`tell application \"Microsoft Outlook\"\n${accountLookupSnippet(acct, true)}\n${messageLookupSnippet(id)}\n set repliedTo to \"\"\n try\n -- Bind the record before reading its field: a nested\n -- 'address of (sender of theMsg)' does not coerce.\n set origSender to sender of theMsg\n set repliedTo to (address of origSender) as string\n end try\n${prelude}\n set theReply to reply to theMsg opening window false ${request.replyAll ? 'reply to all true' : 'without reply to all'}\n -- Neither of these is wrapped in a try. A swallowed account assignment sends\n -- from the default mailbox, and a swallowed insertion files a reply missing\n -- its text — both invisible to the caller if tolerated here.\n set account of theReply to targetAcct\n set content of theReply to my insertAboveQuoted(content of theReply, ${asString(request.html)})\n -- Read the outgoing fields BEFORE acting: sending moves the item, and the\n -- reference can't be read afterwards.\n set toList to {}\n try\n repeat with r in (every to recipient of theReply)\n try\n set ea to email address of r\n set end of toList to (address of ea) as string\n end try\n end repeat\n end try\n set toLine to my joinList(toList, \", \")\n set subj to \"\"\n try\n set subj to (subject of theReply) as string\n end try\n${action}\n return ${asRow(['toLine', 'subj', 'repliedTo', 'filedDraftId'])}\nend tell`, 'standard');\n const parts = summaryFields(raw);\n return {\n to: field(parts, 0),\n subject: field(parts, 1),\n repliedToSender: field(parts, 2),\n draftEntryId: field(parts, 3),\n };\n}\n"]}
|
package/dist/mac/templates.js
CHANGED
|
@@ -11,21 +11,12 @@ exports.editEmailTemplate = editEmailTemplate;
|
|
|
11
11
|
// two platforms.
|
|
12
12
|
const run_1 = require("./run");
|
|
13
13
|
const scripts_1 = require("./scripts");
|
|
14
|
+
const templateLimits_1 = require("../shared/templateLimits");
|
|
14
15
|
const templateBody_1 = require("../templateBody");
|
|
15
16
|
const errors_1 = require("../errors");
|
|
16
|
-
/**
|
|
17
|
-
const TEMPLATE_SEARCH_DEPTH = 3;
|
|
18
|
-
/** Cap on one template body, matching Windows. */
|
|
19
|
-
const MAX_TEMPLATE_HTML = 100_000;
|
|
20
|
-
/** Plain text standing in for a body that wasn't asked for. */
|
|
21
|
-
const PREVIEW_CHARS = 200;
|
|
22
|
-
/**
|
|
23
|
-
* Embedded (cid:) images live as attachments on the TEMPLATE item, so its HTML
|
|
24
|
-
* reused elsewhere shows broken-image placeholders. Template emails are text
|
|
25
|
-
* and markup only.
|
|
26
|
-
*/
|
|
17
|
+
/** A template body without its embedded images — see EMBEDDED_IMAGE_SOURCE. */
|
|
27
18
|
function stripEmbeddedImages(html) {
|
|
28
|
-
return html.replace(
|
|
19
|
+
return html.replace(new RegExp(templateLimits_1.EMBEDDED_IMAGE_SOURCE, 'gi'), '');
|
|
29
20
|
}
|
|
30
21
|
/**
|
|
31
22
|
* The template emails in a mailbox folder, newest first; a missing folder is
|
|
@@ -43,7 +34,7 @@ tell application "Microsoft Outlook"
|
|
|
43
34
|
${(0, scripts_1.accountLookupSnippet)(acct)}
|
|
44
35
|
${(0, scripts_1.rootFolderSnippet)(acct, 'root folder', 'rootFolder')}
|
|
45
36
|
end tell
|
|
46
|
-
set theFolder to my findFolderByName(rootFolder, ${(0, run_1.asString)(request.folder)}, ${(0, run_1.asInt)(TEMPLATE_SEARCH_DEPTH)})
|
|
37
|
+
set theFolder to my findFolderByName(rootFolder, ${(0, run_1.asString)(request.folder)}, ${(0, run_1.asInt)(templateLimits_1.TEMPLATE_SEARCH_DEPTH)})
|
|
47
38
|
tell application "Microsoft Outlook"
|
|
48
39
|
if theFolder is missing value then
|
|
49
40
|
set names to {}
|
|
@@ -62,7 +53,10 @@ tell application "Microsoft Outlook"
|
|
|
62
53
|
set modList to {}
|
|
63
54
|
end try
|
|
64
55
|
set hasMods to ((count of modList) is (count of idList))
|
|
65
|
-
|
|
56
|
+
-- The folder may sit up to the search depth down, so its whole chain is
|
|
57
|
+
-- reported, not just its name: the path is the one Windows' FolderPath gives.
|
|
58
|
+
${(0, scripts_1.folderChainSnippet)(' ')}
|
|
59
|
+
set out to ${(0, run_1.asRow)(['"1"', 'my sanitizeList(chainNames)'])}
|
|
66
60
|
repeat with i from 1 to (count of idList)
|
|
67
61
|
set modAt to ""
|
|
68
62
|
if hasMods then set modAt to my isoDate(item i of modList)
|
|
@@ -74,7 +68,10 @@ end tell`, 'standard'));
|
|
|
74
68
|
if ((0, run_1.field)(header, 0) !== '1') {
|
|
75
69
|
return { folderFound: false, folderPath: '', templates: [], availableFolders: (0, run_1.splitList)((0, run_1.field)(header, 1)) };
|
|
76
70
|
}
|
|
77
|
-
const
|
|
71
|
+
const chain = (0, run_1.splitList)((0, run_1.field)(header, 1)).reverse();
|
|
72
|
+
const folderPath = chain.length
|
|
73
|
+
? (0, scripts_1.macFolderPath)(request.account, chain[0], chain.slice(1))
|
|
74
|
+
: (0, scripts_1.macFolderPath)(request.account, request.folder, []);
|
|
78
75
|
const wanted = (request.subject ?? '').toLowerCase();
|
|
79
76
|
const chosen = records.slice(1)
|
|
80
77
|
.map(record => {
|
|
@@ -93,10 +90,7 @@ end tell`, 'standard'));
|
|
|
93
90
|
const raw = await (0, run_1.runOsaScript)(`tell application "Microsoft Outlook"
|
|
94
91
|
set out to ""
|
|
95
92
|
repeat with theId in ${(0, run_1.asIdList)(chosen.map(item => item.id))}
|
|
96
|
-
|
|
97
|
-
try
|
|
98
|
-
set theMsg to message id theId
|
|
99
|
-
end try
|
|
93
|
+
${(0, scripts_1.messageByIdSnippet)(' ')}
|
|
100
94
|
if theMsg is not missing value then
|
|
101
95
|
set bodyText to ""
|
|
102
96
|
try
|
|
@@ -113,7 +107,7 @@ end tell`, 'standard');
|
|
|
113
107
|
}
|
|
114
108
|
const templates = chosen.map(item => {
|
|
115
109
|
const body = bodies.get(item.id) ?? '';
|
|
116
|
-
const html = request.includeBody ? stripEmbeddedImages(body).slice(0, MAX_TEMPLATE_HTML) : '';
|
|
110
|
+
const html = request.includeBody ? stripEmbeddedImages(body).slice(0, templateLimits_1.MAX_TEMPLATE_HTML) : '';
|
|
117
111
|
// With the HTML in hand it is authoritative (markers survive Word
|
|
118
112
|
// splitting them across tags); without it, the plain text is scanned,
|
|
119
113
|
// where a marker can't have been split.
|
|
@@ -122,7 +116,7 @@ end tell`, 'standard');
|
|
|
122
116
|
entryId: item.id,
|
|
123
117
|
subject: item.subject,
|
|
124
118
|
htmlBody: html,
|
|
125
|
-
bodyPreview: request.includeBody ? '' : body.trim().slice(0,
|
|
119
|
+
bodyPreview: request.includeBody ? '' : body.trim().slice(0, templateLimits_1.TEMPLATE_PREVIEW_CHARS),
|
|
126
120
|
sections: markers.sections,
|
|
127
121
|
placeholders: markers.placeholders,
|
|
128
122
|
lastModified: item.lastModified,
|
|
@@ -141,7 +135,7 @@ tell application "Microsoft Outlook"
|
|
|
141
135
|
${(0, scripts_1.accountLookupSnippet)(acct)}
|
|
142
136
|
${(0, scripts_1.rootFolderSnippet)(acct, 'root folder', 'rootFolder')}
|
|
143
137
|
end tell
|
|
144
|
-
set theFolder to my findFolderByName(rootFolder, ${(0, run_1.asString)(request.folder)}, ${(0, run_1.asInt)(TEMPLATE_SEARCH_DEPTH)})
|
|
138
|
+
set theFolder to my findFolderByName(rootFolder, ${(0, run_1.asString)(request.folder)}, ${(0, run_1.asInt)(templateLimits_1.TEMPLATE_SEARCH_DEPTH)})
|
|
145
139
|
tell application "Microsoft Outlook"
|
|
146
140
|
set wasCreated to false
|
|
147
141
|
if theFolder is missing value then
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"templates.js","sourceRoot":"","sources":["../../src/mac/templates.ts"],"names":[],"mappings":"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","sourcesContent":["// Template emails: ordinary mail items kept in a mailbox folder and reused as\n// email bodies, plus the compose-window editor for one.\n//\n// Outlook for Mac serves a message's HTML through `content` — the same thing\n// COM's HTMLBody returns — which is what makes templates portable between the\n// two platforms.\nimport {\n asIdList,\n asInt,\n asRow,\n asString,\n boolField,\n field,\n runOsaScript,\n splitFields,\n splitList,\n splitRecords,\n summaryFields\n} from './run';\nimport {\n accountLookupSnippet,\n FIND_FOLDER_HANDLER,\n macFolderPath,\n resolveMacAccount,\n rootFolderSnippet\n} from './scripts';\nimport { findTemplateMarkers } from '../templateBody';\nimport { NotFoundError } from '../errors';\nimport type { EditTemplateRequest, ReadTemplatesRequest, SaveTemplateRequest } from '../backend';\nimport type { SaveTemplateResult, TemplateEmail, TemplateFolderResult } from '../types';\n\n/** How deep under the mailbox root a template folder is searched for. */\nconst TEMPLATE_SEARCH_DEPTH = 3;\n\n/** Cap on one template body, matching Windows. */\nconst MAX_TEMPLATE_HTML = 100_000;\n\n/** Plain text standing in for a body that wasn't asked for. */\nconst PREVIEW_CHARS = 200;\n\n/**\n * Embedded (cid:) images live as attachments on the TEMPLATE item, so its HTML\n * reused elsewhere shows broken-image placeholders. Template emails are text\n * and markup only.\n */\nfunction stripEmbeddedImages(html: string): string {\n return html.replace(/<img[^>]*src=\"cid:[^\"]*\"[^>]*>/gi, '');\n}\n\n/**\n * The template emails in a mailbox folder, newest first; a missing folder is\n * reported as folderFound:false with the mailbox's top-level folder names.\n *\n * Bodies are read in a second pass, for the chosen templates only: a folder of\n * Word-sized templates is megabytes, and pulling all of them through stdout on\n * a call that wanted one is what overruns the output limit.\n */\nexport async function readTemplateEmails(request: ReadTemplatesRequest): Promise<TemplateFolderResult> {\n const acct = await resolveMacAccount(request.account);\n // Pass 1 — find the folder and index it: three bulk reads, no bodies.\n const records = splitRecords(await runOsaScript(`${FIND_FOLDER_HANDLER}\ntell application \"Microsoft Outlook\"\n${accountLookupSnippet(acct)}\n${rootFolderSnippet(acct, 'root folder', 'rootFolder')}\nend tell\nset theFolder to my findFolderByName(rootFolder, ${asString(request.folder)}, ${asInt(TEMPLATE_SEARCH_DEPTH)})\ntell application \"Microsoft Outlook\"\n if theFolder is missing value then\n set names to {}\n repeat with f in (mail folders of rootFolder)\n try\n set end of names to (name of f) as string\n end try\n end repeat\n return ${asRow(['\"0\"', 'my sanitizeList(names)'])}\n end if\n set idList to id of every message of theFolder\n set subjList to subject of every message of theFolder\n try\n set modList to modification date of every message of theFolder\n on error\n set modList to {}\n end try\n set hasMods to ((count of modList) is (count of idList))\n set out to ${asRow(['\"1\"', '(name of theFolder as string)'])}\n repeat with i from 1 to (count of idList)\n set modAt to \"\"\n if hasMods then set modAt to my isoDate(item i of modList)\n set out to out & ${asRow(['(item i of idList as string)', '(item i of subjList)', 'modAt'])}\n end repeat\n return out\nend tell`, 'standard'));\n\n const header = splitFields(records[0] ?? '');\n if (field(header, 0) !== '1') {\n return {folderFound: false, folderPath: '', templates: [], availableFolders: splitList(field(header, 1))};\n }\n const folderPath = macFolderPath(request.account, field(header, 1) || request.folder, []);\n const wanted = (request.subject ?? '').toLowerCase();\n const chosen = records.slice(1)\n .map(record => {\n const parts = splitFields(record);\n return {id: field(parts, 0), subject: field(parts, 1).trim(), lastModified: field(parts, 2)};\n })\n // Newest first, before the cap, so \"the newest 20\" means that.\n .sort((a, b) => b.lastModified.localeCompare(a.lastModified))\n .filter(item => !wanted || item.subject.toLowerCase() === wanted)\n .slice(0, request.limit);\n if (chosen.length === 0) return {folderFound: true, folderPath, templates: [], availableFolders: []};\n\n // Pass 2 — the bodies, for the chosen templates only.\n const bodyProperty = request.includeBody ? 'content' : 'plain text content';\n const bodies = new Map<string, string>();\n const raw = await runOsaScript(`tell application \"Microsoft Outlook\"\n set out to \"\"\n repeat with theId in ${asIdList(chosen.map(item => item.id))}\n set theMsg to missing value\n try\n set theMsg to message id theId\n end try\n if theMsg is not missing value then\n set bodyText to \"\"\n try\n set bodyText to (${bodyProperty} of theMsg) as string\n end try\n set out to out & ${asRow(['(theId as string)', 'bodyText'])}\n end if\n end repeat\n return out\nend tell`, 'standard');\n for (const record of splitRecords(raw)) {\n const parts = splitFields(record);\n bodies.set(field(parts, 0), field(parts, 1));\n }\n\n const templates: TemplateEmail[] = chosen.map(item => {\n const body = bodies.get(item.id) ?? '';\n const html = request.includeBody ? stripEmbeddedImages(body).slice(0, MAX_TEMPLATE_HTML) : '';\n // With the HTML in hand it is authoritative (markers survive Word\n // splitting them across tags); without it, the plain text is scanned,\n // where a marker can't have been split.\n const markers = findTemplateMarkers(request.includeBody ? html : body);\n return {\n entryId: item.id,\n subject: item.subject,\n htmlBody: html,\n bodyPreview: request.includeBody ? '' : body.trim().slice(0, PREVIEW_CHARS),\n sections: markers.sections,\n placeholders: markers.placeholders,\n lastModified: item.lastModified,\n };\n });\n return {folderFound: true, folderPath, templates, availableFolders: []};\n}\n\n/**\n * Save a new template email into a folder, creating the folder at the mailbox\n * root when absent. Always adds a new item — never overwrites one.\n */\nexport async function saveTemplateEmail(request: SaveTemplateRequest): Promise<SaveTemplateResult> {\n const acct = await resolveMacAccount(request.account);\n const parts = summaryFields(await runOsaScript(`${FIND_FOLDER_HANDLER}\ntell application \"Microsoft Outlook\"\n${accountLookupSnippet(acct)}\n${rootFolderSnippet(acct, 'root folder', 'rootFolder')}\nend tell\nset theFolder to my findFolderByName(rootFolder, ${asString(request.folder)}, ${asInt(TEMPLATE_SEARCH_DEPTH)})\ntell application \"Microsoft Outlook\"\n set wasCreated to false\n if theFolder is missing value then\n set theFolder to (make new mail folder at rootFolder with properties {name:${asString(request.folder)}})\n set wasCreated to true\n end if\n set countBefore to count of messages of theFolder\n set newMsg to make new outgoing message with properties {subject:${asString(request.subject)}, content:${asString(request.htmlBody)}}\n try\n set account of newMsg to targetAcct\n end try\n -- 'move' reports success even where it silently does nothing, so a template\n -- that was never filed must not be reported as saved.\n move newMsg to theFolder\n if (count of messages of theFolder) is not greater than countBefore then error \"Outlook did not file the template in '\" & (name of theFolder) & \"'.\"\n return ${asRow(['(name of theFolder as string)', '(wasCreated as string)'])}\nend tell`, 'standard'));\n return {\n folderPath: macFolderPath(request.account, field(parts, 0) || request.folder, []),\n folderCreated: boolField(parts, 1),\n };\n}\n\n/**\n * Open a compose window pre-filled with the HTML, wait for it to close, and\n * return the saved draft's HTML. On macOS the edit is kept by answering\n * Outlook's own \"save this message?\" prompt on close.\n */\nexport async function editEmailTemplate(request: EditTemplateRequest): Promise<string> {\n const subject = `${request.label} - Save and close when done`;\n const saved = await runOsaScript(`tell application \"Microsoft Outlook\"\n set editSubject to ${asString(subject)}\n set draftMsg to make new outgoing message with properties {subject:editSubject, content:${asString(request.html)}}\n open draftMsg\n activate\nend tell\n\n-- Poll until the compose window is gone, identifying ours by subject rather\n-- than counting windows the person may also have open.\nset stillOpen to true\nrepeat while stillOpen\n delay 0.7\n set stillOpen to false\n tell application \"Microsoft Outlook\"\n try\n repeat with w in draft windows\n try\n if ((subject of (object of w)) as string) is editSubject then\n set stillOpen to true\n exit repeat\n end if\n end try\n end repeat\n end try\n end tell\nend repeat\n\ntell application \"Microsoft Outlook\"\n -- Prefer the saved draft: it is what the person chose to keep, and it\n -- outlives the window. Every editor draft is removed so none pile up.\n set savedHtml to \"\"\n try\n repeat with acctList in {exchange accounts, imap accounts, pop accounts}\n repeat with a in acctList\n try\n repeat with m in (messages of (drafts of a))\n try\n if ((subject of m) as string) is editSubject then\n if savedHtml is \"\" then set savedHtml to (content of m) as string\n delete m\n end if\n end try\n end repeat\n end try\n end repeat\n end repeat\n end try\n return my sanitize(savedHtml)\nend tell`, 'interactive');\n if (!saved) {\n throw new NotFoundError('template', 'No template was saved. Save the draft before closing the window.');\n }\n return saved;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"templates.js","sourceRoot":"","sources":["../../src/mac/templates.ts"],"names":[],"mappings":"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","sourcesContent":["// Template emails: ordinary mail items kept in a mailbox folder and reused as\n// email bodies, plus the compose-window editor for one.\n//\n// Outlook for Mac serves a message's HTML through `content` — the same thing\n// COM's HTMLBody returns — which is what makes templates portable between the\n// two platforms.\nimport {\n asIdList,\n asInt,\n asRow,\n asString,\n boolField,\n field,\n runOsaScript,\n splitFields,\n splitList,\n splitRecords,\n summaryFields,\n} from './run';\nimport {\n accountLookupSnippet,\n FIND_FOLDER_HANDLER,\n folderChainSnippet,\n macFolderPath,\n messageByIdSnippet,\n resolveMacAccount,\n rootFolderSnippet,\n} from './scripts';\nimport {\n EMBEDDED_IMAGE_SOURCE,\n MAX_TEMPLATE_HTML,\n TEMPLATE_PREVIEW_CHARS,\n TEMPLATE_SEARCH_DEPTH,\n} from '../shared/templateLimits';\nimport { findTemplateMarkers } from '../templateBody';\nimport { NotFoundError } from '../errors';\nimport type { EditTemplateRequest, ReadTemplatesRequest, SaveTemplateRequest } from '../backend';\nimport type { SaveTemplateResult, TemplateEmail, TemplateFolderResult } from '../types';\n\n/** A template body without its embedded images — see EMBEDDED_IMAGE_SOURCE. */\nfunction stripEmbeddedImages(html: string): string {\n return html.replace(new RegExp(EMBEDDED_IMAGE_SOURCE, 'gi'), '');\n}\n\n/**\n * The template emails in a mailbox folder, newest first; a missing folder is\n * reported as folderFound:false with the mailbox's top-level folder names.\n *\n * Bodies are read in a second pass, for the chosen templates only: a folder of\n * Word-sized templates is megabytes, and pulling all of them through stdout on\n * a call that wanted one is what overruns the output limit.\n */\nexport async function readTemplateEmails(request: ReadTemplatesRequest): Promise<TemplateFolderResult> {\n const acct = await resolveMacAccount(request.account);\n // Pass 1 — find the folder and index it: three bulk reads, no bodies.\n const records = splitRecords(await runOsaScript(`${FIND_FOLDER_HANDLER}\ntell application \"Microsoft Outlook\"\n${accountLookupSnippet(acct)}\n${rootFolderSnippet(acct, 'root folder', 'rootFolder')}\nend tell\nset theFolder to my findFolderByName(rootFolder, ${asString(request.folder)}, ${asInt(TEMPLATE_SEARCH_DEPTH)})\ntell application \"Microsoft Outlook\"\n if theFolder is missing value then\n set names to {}\n repeat with f in (mail folders of rootFolder)\n try\n set end of names to (name of f) as string\n end try\n end repeat\n return ${asRow(['\"0\"', 'my sanitizeList(names)'])}\n end if\n set idList to id of every message of theFolder\n set subjList to subject of every message of theFolder\n try\n set modList to modification date of every message of theFolder\n on error\n set modList to {}\n end try\n set hasMods to ((count of modList) is (count of idList))\n -- The folder may sit up to the search depth down, so its whole chain is\n -- reported, not just its name: the path is the one Windows' FolderPath gives.\n${folderChainSnippet(' ')}\n set out to ${asRow(['\"1\"', 'my sanitizeList(chainNames)'])}\n repeat with i from 1 to (count of idList)\n set modAt to \"\"\n if hasMods then set modAt to my isoDate(item i of modList)\n set out to out & ${asRow(['(item i of idList as string)', '(item i of subjList)', 'modAt'])}\n end repeat\n return out\nend tell`, 'standard'));\n\n const header = splitFields(records[0] ?? '');\n if (field(header, 0) !== '1') {\n return { folderFound: false, folderPath: '', templates: [], availableFolders: splitList(field(header, 1)) };\n }\n const chain = splitList(field(header, 1)).reverse();\n const folderPath = chain.length\n ? macFolderPath(request.account, chain[0], chain.slice(1))\n : macFolderPath(request.account, request.folder, []);\n const wanted = (request.subject ?? '').toLowerCase();\n const chosen = records.slice(1)\n .map(record => {\n const parts = splitFields(record);\n return { id: field(parts, 0), subject: field(parts, 1).trim(), lastModified: field(parts, 2) };\n })\n // Newest first, before the cap, so \"the newest 20\" means that.\n .sort((a, b) => b.lastModified.localeCompare(a.lastModified))\n .filter(item => !wanted || item.subject.toLowerCase() === wanted)\n .slice(0, request.limit);\n if (chosen.length === 0) return { folderFound: true, folderPath, templates: [], availableFolders: [] };\n\n // Pass 2 — the bodies, for the chosen templates only.\n const bodyProperty = request.includeBody ? 'content' : 'plain text content';\n const bodies = new Map<string, string>();\n const raw = await runOsaScript(`tell application \"Microsoft Outlook\"\n set out to \"\"\n repeat with theId in ${asIdList(chosen.map(item => item.id))}\n${messageByIdSnippet(' ')}\n if theMsg is not missing value then\n set bodyText to \"\"\n try\n set bodyText to (${bodyProperty} of theMsg) as string\n end try\n set out to out & ${asRow(['(theId as string)', 'bodyText'])}\n end if\n end repeat\n return out\nend tell`, 'standard');\n for (const record of splitRecords(raw)) {\n const parts = splitFields(record);\n bodies.set(field(parts, 0), field(parts, 1));\n }\n\n const templates: TemplateEmail[] = chosen.map(item => {\n const body = bodies.get(item.id) ?? '';\n const html = request.includeBody ? stripEmbeddedImages(body).slice(0, MAX_TEMPLATE_HTML) : '';\n // With the HTML in hand it is authoritative (markers survive Word\n // splitting them across tags); without it, the plain text is scanned,\n // where a marker can't have been split.\n const markers = findTemplateMarkers(request.includeBody ? html : body);\n return {\n entryId: item.id,\n subject: item.subject,\n htmlBody: html,\n bodyPreview: request.includeBody ? '' : body.trim().slice(0, TEMPLATE_PREVIEW_CHARS),\n sections: markers.sections,\n placeholders: markers.placeholders,\n lastModified: item.lastModified,\n };\n });\n return { folderFound: true, folderPath, templates, availableFolders: [] };\n}\n\n/**\n * Save a new template email into a folder, creating the folder at the mailbox\n * root when absent. Always adds a new item — never overwrites one.\n */\nexport async function saveTemplateEmail(request: SaveTemplateRequest): Promise<SaveTemplateResult> {\n const acct = await resolveMacAccount(request.account);\n const parts = summaryFields(await runOsaScript(`${FIND_FOLDER_HANDLER}\ntell application \"Microsoft Outlook\"\n${accountLookupSnippet(acct)}\n${rootFolderSnippet(acct, 'root folder', 'rootFolder')}\nend tell\nset theFolder to my findFolderByName(rootFolder, ${asString(request.folder)}, ${asInt(TEMPLATE_SEARCH_DEPTH)})\ntell application \"Microsoft Outlook\"\n set wasCreated to false\n if theFolder is missing value then\n set theFolder to (make new mail folder at rootFolder with properties {name:${asString(request.folder)}})\n set wasCreated to true\n end if\n set countBefore to count of messages of theFolder\n set newMsg to make new outgoing message with properties {subject:${asString(request.subject)}, content:${asString(request.htmlBody)}}\n try\n set account of newMsg to targetAcct\n end try\n -- 'move' reports success even where it silently does nothing, so a template\n -- that was never filed must not be reported as saved.\n move newMsg to theFolder\n if (count of messages of theFolder) is not greater than countBefore then error \"Outlook did not file the template in '\" & (name of theFolder) & \"'.\"\n return ${asRow(['(name of theFolder as string)', '(wasCreated as string)'])}\nend tell`, 'standard'));\n return {\n folderPath: macFolderPath(request.account, field(parts, 0) || request.folder, []),\n folderCreated: boolField(parts, 1),\n };\n}\n\n/**\n * Open a compose window pre-filled with the HTML, wait for it to close, and\n * return the saved draft's HTML. On macOS the edit is kept by answering\n * Outlook's own \"save this message?\" prompt on close.\n */\nexport async function editEmailTemplate(request: EditTemplateRequest): Promise<string> {\n const subject = `${request.label} - Save and close when done`;\n const saved = await runOsaScript(`tell application \"Microsoft Outlook\"\n set editSubject to ${asString(subject)}\n set draftMsg to make new outgoing message with properties {subject:editSubject, content:${asString(request.html)}}\n open draftMsg\n activate\nend tell\n\n-- Poll until the compose window is gone, identifying ours by subject rather\n-- than counting windows the person may also have open.\nset stillOpen to true\nrepeat while stillOpen\n delay 0.7\n set stillOpen to false\n tell application \"Microsoft Outlook\"\n try\n repeat with w in draft windows\n try\n if ((subject of (object of w)) as string) is editSubject then\n set stillOpen to true\n exit repeat\n end if\n end try\n end repeat\n end try\n end tell\nend repeat\n\ntell application \"Microsoft Outlook\"\n -- Prefer the saved draft: it is what the person chose to keep, and it\n -- outlives the window. Every editor draft is removed so none pile up.\n set savedHtml to \"\"\n try\n repeat with acctList in {exchange accounts, imap accounts, pop accounts}\n repeat with a in acctList\n try\n repeat with m in (messages of (drafts of a))\n try\n if ((subject of m) as string) is editSubject then\n if savedHtml is \"\" then set savedHtml to (content of m) as string\n delete m\n end if\n end try\n end repeat\n end try\n end repeat\n end repeat\n end try\n return my sanitize(savedHtml)\nend tell`, 'interactive');\n if (!saved) {\n throw new NotFoundError('template', 'No template was saved. Save the draft before closing the window.');\n }\n return saved;\n}\n"]}
|
package/dist/mail.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { MailFolderRef } from './types';
|
|
1
|
+
import type { DeleteMailOutcome, DeleteMailResult, MailFolderRef } from './types';
|
|
2
2
|
/**
|
|
3
3
|
* Outlook's olDefaultFolders ids for the folders this package addresses by
|
|
4
4
|
* role rather than by name. The ids are Outlook's own, and a folder's NAME is
|
|
@@ -18,13 +18,21 @@ export declare const FolderId: Readonly<{
|
|
|
18
18
|
* same name; reach the latter by qualifying it (`Inbox\Drafts`).
|
|
19
19
|
*/
|
|
20
20
|
export declare const WELL_KNOWN_FOLDERS: Readonly<Record<string, number>>;
|
|
21
|
-
/**
|
|
22
|
-
|
|
21
|
+
/**
|
|
22
|
+
* Roots whose contents are received or sent mail rather than working state —
|
|
23
|
+
* what `deleteOutlookEmails` refuses to touch, subfolders included, unless
|
|
24
|
+
* allowed. Matched by id on both platforms, as every role is.
|
|
25
|
+
*/
|
|
26
|
+
export declare const PROTECTED_ROOTS: readonly number[];
|
|
23
27
|
/**
|
|
24
28
|
* Why `deleteOutlookEmails` refuses an item, worded once for both platforms —
|
|
25
29
|
* it reaches the caller verbatim as a per-item `reason`.
|
|
26
30
|
*/
|
|
27
31
|
export declare const PROTECTED_MAIL_REASON = "received or sent mail (Inbox/Sent Items or a subfolder) - pass allowProtected to override";
|
|
32
|
+
/** The status a delete script reported for one item; anything it shouldn't have said is a failure. */
|
|
33
|
+
export declare function deleteStatus(reported: string): DeleteMailOutcome['status'];
|
|
34
|
+
/** A deleteOutlookEmails result: every outcome, and how many ended each way. */
|
|
35
|
+
export declare function deleteMailResult(dryRun: boolean, items: DeleteMailOutcome[]): DeleteMailResult;
|
|
28
36
|
/**
|
|
29
37
|
* Roots whose items are outgoing mail, which carries a sent time where incoming
|
|
30
38
|
* mail carries a received one — so a date filter has to know which it's on.
|
package/dist/mail.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.REPLY_PREFIX = exports.REPLY_PREFIX_SOURCE = exports.NON_INCOMING_ROOTS = exports.PROTECTED_MAIL_REASON = exports.
|
|
3
|
+
exports.REPLY_PREFIX = exports.REPLY_PREFIX_SOURCE = exports.NON_INCOMING_ROOTS = exports.PROTECTED_MAIL_REASON = exports.PROTECTED_ROOTS = exports.WELL_KNOWN_FOLDERS = exports.FolderId = void 0;
|
|
4
|
+
exports.deleteStatus = deleteStatus;
|
|
5
|
+
exports.deleteMailResult = deleteMailResult;
|
|
4
6
|
exports.isOutgoingRoot = isOutgoingRoot;
|
|
5
7
|
exports.hasReplyPrefix = hasReplyPrefix;
|
|
6
8
|
exports.threadSubject = threadSubject;
|
|
@@ -9,9 +11,10 @@ exports.folderLeafName = folderLeafName;
|
|
|
9
11
|
exports.mailFolderRef = mailFolderRef;
|
|
10
12
|
exports.folderOption = folderOption;
|
|
11
13
|
exports.splitQuotedOriginal = splitQuotedOriginal;
|
|
12
|
-
// Platform-neutral mail helpers: folder strings, reply prefixes,
|
|
13
|
-
// reply from the thread it quotes
|
|
14
|
-
// shared by both platforms so neither can
|
|
14
|
+
// Platform-neutral mail helpers: folder strings, reply prefixes, splitting a
|
|
15
|
+
// reply from the thread it quotes, and what deleting mail reports. Pure
|
|
16
|
+
// functions, usable without Outlook, and shared by both platforms so neither can
|
|
17
|
+
// come to read these differently.
|
|
15
18
|
const errors_1 = require("./errors");
|
|
16
19
|
/**
|
|
17
20
|
* Outlook's olDefaultFolders ids for the folders this package addresses by
|
|
@@ -45,16 +48,32 @@ exports.WELL_KNOWN_FOLDERS = Object.freeze({
|
|
|
45
48
|
'outbox': exports.FolderId.Outbox,
|
|
46
49
|
});
|
|
47
50
|
/** The Inbox root, as a folder reference. */
|
|
48
|
-
|
|
51
|
+
const INBOX_REF = Object.freeze({
|
|
49
52
|
rootId: exports.FolderId.Inbox,
|
|
50
53
|
rootLabel: 'Inbox',
|
|
51
54
|
segments: [],
|
|
52
55
|
});
|
|
56
|
+
/**
|
|
57
|
+
* Roots whose contents are received or sent mail rather than working state —
|
|
58
|
+
* what `deleteOutlookEmails` refuses to touch, subfolders included, unless
|
|
59
|
+
* allowed. Matched by id on both platforms, as every role is.
|
|
60
|
+
*/
|
|
61
|
+
exports.PROTECTED_ROOTS = Object.freeze([exports.FolderId.Inbox, exports.FolderId.SentMail]);
|
|
53
62
|
/**
|
|
54
63
|
* Why `deleteOutlookEmails` refuses an item, worded once for both platforms —
|
|
55
64
|
* it reaches the caller verbatim as a per-item `reason`.
|
|
56
65
|
*/
|
|
57
66
|
exports.PROTECTED_MAIL_REASON = 'received or sent mail (Inbox/Sent Items or a subfolder) - pass allowProtected to override';
|
|
67
|
+
const DELETE_STATUSES = ['deleted', 'would-delete', 'refused', 'failed'];
|
|
68
|
+
/** The status a delete script reported for one item; anything it shouldn't have said is a failure. */
|
|
69
|
+
function deleteStatus(reported) {
|
|
70
|
+
return DELETE_STATUSES.find(status => status === reported) ?? 'failed';
|
|
71
|
+
}
|
|
72
|
+
/** A deleteOutlookEmails result: every outcome, and how many ended each way. */
|
|
73
|
+
function deleteMailResult(dryRun, items) {
|
|
74
|
+
const tally = (status) => items.filter(item => item.status === status).length;
|
|
75
|
+
return { dryRun, deleted: tally('deleted'), refused: tally('refused'), failed: tally('failed'), items };
|
|
76
|
+
}
|
|
58
77
|
/**
|
|
59
78
|
* Roots whose items are outgoing mail, which carries a sent time where incoming
|
|
60
79
|
* mail carries a received one — so a date filter has to know which it's on.
|
|
@@ -173,7 +192,7 @@ function mailFolderRef(folder) {
|
|
|
173
192
|
*/
|
|
174
193
|
function folderOption(folder) {
|
|
175
194
|
if (folder === undefined || folder.trim() === '')
|
|
176
|
-
return { ref:
|
|
195
|
+
return { ref: INBOX_REF, label: 'Inbox' };
|
|
177
196
|
if (folderSegments(folder).length === 0) {
|
|
178
197
|
throw new errors_1.InvalidRequestError(`Folder '${folder}' does not name a folder.`);
|
|
179
198
|
}
|