@dawnlit/outlook-bridge 2.1.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 +11 -6
- package/dist/errors.d.ts +17 -6
- package/dist/errors.js +16 -7
- package/dist/errors.js.map +1 -1
- package/dist/index.d.ts +1 -1
- 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/bounces.js +10 -19
- package/dist/mac/bounces.js.map +1 -1
- package/dist/mac/cleanup.d.ts +11 -3
- package/dist/mac/cleanup.js +26 -17
- package/dist/mac/cleanup.js.map +1 -1
- package/dist/mac/drafts.js +3 -16
- package/dist/mac/drafts.js.map +1 -1
- package/dist/mac/folders.d.ts +5 -0
- package/dist/mac/folders.js +14 -5
- 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.js +14 -26
- package/dist/mac/read.js.map +1 -1
- package/dist/mac/run.d.ts +0 -3
- package/dist/mac/run.js +8 -8
- package/dist/mac/run.js.map +1 -1
- package/dist/mac/scripts.d.ts +46 -0
- package/dist/mac/scripts.js +98 -10
- package/dist/mac/scripts.js.map +1 -1
- package/dist/mac/send.d.ts +2 -1
- package/dist/mac/send.js +9 -3
- package/dist/mac/send.js.map +1 -1
- package/dist/mac/templates.js +1 -4
- package/dist/mac/templates.js.map +1 -1
- package/dist/mail.d.ts +6 -0
- package/dist/mail.js +7 -1
- package/dist/mail.js.map +1 -1
- package/dist/types.d.ts +20 -4
- 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 +3 -3
- package/dist/windows/bounces.js.map +1 -1
- package/dist/windows/cleanup.js +2 -4
- package/dist/windows/cleanup.js.map +1 -1
- package/dist/windows/drafts.js +2 -2
- package/dist/windows/drafts.js.map +1 -1
- package/dist/windows/folders.js +1 -1
- package/dist/windows/folders.js.map +1 -1
- package/dist/windows/json.js.map +1 -0
- package/dist/windows/read.js +17 -14
- package/dist/windows/read.js.map +1 -1
- package/dist/windows/scripts.d.ts +6 -0
- package/dist/windows/scripts.js +11 -2
- package/dist/windows/scripts.js.map +1 -1
- package/dist/windows/send.d.ts +5 -1
- package/dist/windows/send.js +14 -4
- package/dist/windows/send.js.map +1 -1
- package/dist/windows/templates.js +2 -2
- package/dist/windows/templates.js.map +1 -1
- package/package.json +1 -1
- package/dist/shared/json.js.map +0 -1
- /package/dist/{shared → windows}/json.d.ts +0 -0
- /package/dist/{shared → windows}/json.js +0 -0
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/**\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/** 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 * `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${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 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"]}
|
|
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
|
@@ -8,6 +8,7 @@ export declare function sendOutlookEmail(request: SendRequest): Promise<SendEmai
|
|
|
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
|
@@ -97,7 +97,8 @@ end tell`, 'standard');
|
|
|
97
97
|
* `reply to` builds the same quoted body COM's Reply() returns, so the insertion
|
|
98
98
|
* point is the one thing the platforms must agree on (see WORD_SECTION_ANCHOR).
|
|
99
99
|
* `to` comes back as the reply's resolved addresses: Outlook for Mac exposes
|
|
100
|
-
* 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.
|
|
101
102
|
*/
|
|
102
103
|
async function replyOutlookEmail(request) {
|
|
103
104
|
const id = (0, scripts_1.macMessageId)(request.email.entryId);
|
|
@@ -137,9 +138,14 @@ ${prelude}
|
|
|
137
138
|
set subj to (subject of theReply) as string
|
|
138
139
|
end try
|
|
139
140
|
${action}
|
|
140
|
-
return ${(0, run_1.asRow)(['toLine', 'subj', 'repliedTo'])}
|
|
141
|
+
return ${(0, run_1.asRow)(['toLine', 'subj', 'repliedTo', 'filedDraftId'])}
|
|
141
142
|
end tell`, 'standard');
|
|
142
143
|
const parts = (0, run_1.summaryFields)(raw);
|
|
143
|
-
return {
|
|
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
|
+
};
|
|
144
150
|
}
|
|
145
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":";;AAyEA,4CAoBC;
|
|
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
|
@@ -90,10 +90,7 @@ end tell`, 'standard'));
|
|
|
90
90
|
const raw = await (0, run_1.runOsaScript)(`tell application "Microsoft Outlook"
|
|
91
91
|
set out to ""
|
|
92
92
|
repeat with theId in ${(0, run_1.asIdList)(chosen.map(item => item.id))}
|
|
93
|
-
|
|
94
|
-
try
|
|
95
|
-
set theMsg to message id theId
|
|
96
|
-
end try
|
|
93
|
+
${(0, scripts_1.messageByIdSnippet)(' ')}
|
|
97
94
|
if theMsg is not missing value then
|
|
98
95
|
set bodyText to ""
|
|
99
96
|
try
|
|
@@ -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 folderChainSnippet,\n macFolderPath,\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 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, 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"]}
|
|
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
|
@@ -18,6 +18,12 @@ 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
|
+
* 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[];
|
|
21
27
|
/**
|
|
22
28
|
* Why `deleteOutlookEmails` refuses an item, worded once for both platforms —
|
|
23
29
|
* it reaches the caller verbatim as a per-item `reason`.
|
package/dist/mail.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
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.WELL_KNOWN_FOLDERS = exports.FolderId = void 0;
|
|
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
4
|
exports.deleteStatus = deleteStatus;
|
|
5
5
|
exports.deleteMailResult = deleteMailResult;
|
|
6
6
|
exports.isOutgoingRoot = isOutgoingRoot;
|
|
@@ -53,6 +53,12 @@ const INBOX_REF = Object.freeze({
|
|
|
53
53
|
rootLabel: 'Inbox',
|
|
54
54
|
segments: [],
|
|
55
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]);
|
|
56
62
|
/**
|
|
57
63
|
* Why `deleteOutlookEmails` refuses an item, worded once for both platforms —
|
|
58
64
|
* it reaches the caller verbatim as a per-item `reason`.
|
package/dist/mail.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mail.js","sourceRoot":"","sources":["../src/mail.ts"],"names":[],"mappings":"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","sourcesContent":["// Platform-neutral mail helpers: folder strings, reply prefixes, splitting a\n// reply from the thread it quotes, and what deleting mail reports. Pure\n// functions, usable without Outlook, and shared by both platforms so neither can\n// come to read these differently.\nimport { InvalidRequestError } from './errors';\nimport type { DeleteMailOutcome, DeleteMailResult, MailFolderRef } from './types';\n\n/**\n * Outlook's olDefaultFolders ids for the folders this package addresses by\n * role rather than by name. The ids are Outlook's own, and a folder's NAME is\n * localized, so a role is always resolved through its id.\n */\nexport const FolderId = Object.freeze({\n DeletedItems: 3,\n Outbox: 4,\n SentMail: 5,\n Inbox: 6,\n Drafts: 16,\n Junk: 23,\n});\n\n/**\n * The folders addressable by name rather than by walking from the Inbox, with\n * their olDefaultFolders id. A well-known name wins over a user folder of the\n * same name; reach the latter by qualifying it (`Inbox\\Drafts`).\n */\nexport const WELL_KNOWN_FOLDERS: Readonly<Record<string, number>> = Object.freeze({\n 'inbox': FolderId.Inbox,\n 'sent items': FolderId.SentMail,\n 'sent': FolderId.SentMail,\n 'sent mail': FolderId.SentMail,\n 'drafts': FolderId.Drafts,\n 'deleted items': FolderId.DeletedItems,\n 'deleted': FolderId.DeletedItems,\n 'trash': FolderId.DeletedItems,\n 'junk email': FolderId.Junk,\n 'junk': FolderId.Junk,\n 'outbox': FolderId.Outbox,\n});\n\n/** The Inbox root, as a folder reference. */\nconst INBOX_REF: Readonly<MailFolderRef> = Object.freeze({\n rootId: FolderId.Inbox,\n rootLabel: 'Inbox',\n segments: [],\n});\n\n/**\n * Why `deleteOutlookEmails` refuses an item, worded once for both platforms —\n * it reaches the caller verbatim as a per-item `reason`.\n */\nexport const PROTECTED_MAIL_REASON =\n 'received or sent mail (Inbox/Sent Items or a subfolder) - pass allowProtected to override';\n\nconst DELETE_STATUSES: readonly DeleteMailOutcome['status'][] = ['deleted', 'would-delete', 'refused', 'failed'];\n\n/** The status a delete script reported for one item; anything it shouldn't have said is a failure. */\nexport function deleteStatus(reported: string): DeleteMailOutcome['status'] {\n return DELETE_STATUSES.find(status => status === reported) ?? 'failed';\n}\n\n/** A deleteOutlookEmails result: every outcome, and how many ended each way. */\nexport function deleteMailResult(dryRun: boolean, items: DeleteMailOutcome[]): DeleteMailResult {\n const tally = (status: DeleteMailOutcome['status']) => items.filter(item => item.status === status).length;\n return { dryRun, deleted: tally('deleted'), refused: tally('refused'), failed: tally('failed'), items };\n}\n\n/**\n * Roots whose items are outgoing mail, which carries a sent time where incoming\n * mail carries a received one — so a date filter has to know which it's on.\n */\nexport function isOutgoingRoot(rootId: number): boolean {\n return rootId === FolderId.SentMail || rootId === FolderId.Outbox || rootId === FolderId.Drafts;\n}\n\n/**\n * Roots an Inbox walk must never descend into.\n *\n * On an Exchange profile these sit beside the Inbox and a walk from it can't\n * reach them anyway. On an IMAP profile Outlook nests them UNDER the Inbox, so\n * the same walk would hand back mail already sent, drafted or thrown away as if\n * it had just arrived. They are matched by id, never by name: the names are\n * localized.\n */\nexport const NON_INCOMING_ROOTS: readonly number[] = Object.freeze([\n FolderId.DeletedItems,\n FolderId.Outbox,\n FolderId.SentMail,\n FolderId.Drafts,\n FolderId.Junk,\n]);\n\n/**\n * Subject prefixes that mark a reply or a forward.\n *\n * Kept as a pattern SOURCE because it runs in two engines: macOS tests it as a\n * JavaScript RegExp, Windows inside PowerShell's .NET one, and both read every\n * construct used here the same way. Non-ASCII is written as \\uXXXX so the\n * generated PowerShell stays plain ASCII.\n *\n * Not only English: mail threaded in Chinese, Japanese or Korean replies under\n * 回复:/返信:/답장: and forwards under 转发:/転送:/전달:, and the European\n * clients use AW:, WG:, SV:, TR:, RV:, ENC: and friends. The colon may be the\n * full-width one a CJK input method types, and Outlook's reply counter (RE[2]:)\n * can sit before it.\n */\nconst REPLY_PREFIXES: readonly string[] = [\n 're', 'fwd?', 'aw', 'wg', 'sv', 'vs', 'vb', 'tr', 'res', 'rv', 'enc', 'odp',\n '\\\\u56DE\\\\u590D', '\\\\u56DE\\\\u8986', '\\\\u7B54\\\\u590D', '\\\\u7B54\\\\u8986',\n '\\\\u8F6C\\\\u53D1', '\\\\u8F49\\\\u767C', '\\\\u8FD4\\\\u4FE1', '\\\\u8EE2\\\\u9001',\n '\\\\uB2F5\\\\uC7A5', '\\\\uC804\\\\uB2EC',\n];\n\n/** The reply/forward prefixes as one case-insensitive pattern source. */\nexport const REPLY_PREFIX_SOURCE =\n '^\\\\s*(?:' + REPLY_PREFIXES.join('|') + ')\\\\s*(?:\\\\[\\\\d+\\\\])?\\\\s*[:\\\\uFF1A]';\nexport const REPLY_PREFIX = new RegExp(REPLY_PREFIX_SOURCE, 'i');\n\n/**\n * Whether a subject opens on a reply or forward prefix.\n *\n * This reads what the subject claims, not how the email was made: a forward\n * carries a prefix too, and a client can leave one off a real reply.\n */\nexport function hasReplyPrefix(subject: string): boolean {\n return REPLY_PREFIX.test(subject);\n}\n\n/**\n * The subject every message in a thread shares: every reply and forward prefix\n * stripped, however many clients stacked them (`RE: FW: RE[2]: Rate request`\n * is `Rate request`), and whitespace collapsed. Case is kept, so lower-case\n * both sides to compare two threads.\n */\nexport function threadSubject(subject: string): string {\n let rest = subject;\n // Each pass removes at least the prefix's colon, so this always ends.\n while (REPLY_PREFIX.test(rest)) rest = rest.replace(REPLY_PREFIX, '');\n return rest.replace(/\\s+/g, ' ').trim();\n}\n\n/**\n * A subject glob as an anchored regex source: `*` matches any run, `?` one\n * character, everything else itself. Written with constructs JavaScript and\n * .NET read identically, so both platforms apply exactly the same test.\n */\nexport function subjectGlobSource(glob: string): string {\n let source = '';\n for (const ch of glob) {\n if (ch === '*') source += '.*';\n else if (ch === '?') source += '.';\n else source += ch.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n }\n return `^${source}$`;\n}\n\n/** The leaf name of a folder path or bare name. */\nexport function folderLeafName(entry: string): string {\n return entry.trim().split(/[\\\\/]/).filter(Boolean).pop() ?? '';\n}\n\n/** A full Outlook folder path starts with the mailbox: `\\\\mailbox@example.com\\…`. */\nconst MAILBOX_PREFIX = /^\\\\\\\\[^\\\\/]+[\\\\/]/;\n\nfunction folderSegments(folder: string): string[] {\n return folder.trim().replace(MAILBOX_PREFIX, '').split(/[\\\\/]+/).map(s => s.trim()).filter(Boolean);\n}\n\n/**\n * Resolve a folder string to a well-known root plus the segments below it.\n *\n * Accepts every shape a person has to hand: a bare name ('Invoices'), a\n * relative path ('Invoices\\Paid'), a well-known folder ('Sent Items'), a path\n * under one ('Deleted Items\\2026'), and the full path listInboxFolders prints\n * ('\\\\team@example.com\\Inbox\\Invoices'). Anything not starting at a well-known\n * folder is taken as relative to the Inbox.\n */\nexport function mailFolderRef(folder: string): MailFolderRef {\n const segments = folderSegments(folder);\n const head = segments[0]?.toLowerCase();\n if (head !== undefined && Object.prototype.hasOwnProperty.call(WELL_KNOWN_FOLDERS, head)) {\n return { rootId: WELL_KNOWN_FOLDERS[head], rootLabel: segments[0], segments: segments.slice(1) };\n }\n return { rootId: FolderId.Inbox, rootLabel: 'Inbox', segments };\n}\n\n/**\n * A caller's folder option as a reference: none given means the Inbox root,\n * and a string that names no folder at all (only separators) is refused rather\n * than silently read as the Inbox root.\n */\nexport function folderOption(folder: string | undefined): { ref: MailFolderRef; label: string } {\n if (folder === undefined || folder.trim() === '') return { ref: INBOX_REF, label: 'Inbox' };\n if (folderSegments(folder).length === 0) {\n throw new InvalidRequestError(`Folder '${folder}' does not name a folder.`);\n }\n return { ref: mailFolderRef(folder), label: folder.trim() };\n}\n\n/**\n * Split a plain-text reply into the sender's own text and the quoted thread\n * below it.\n *\n * This matters more than it looks: the quoted original carries the text of the\n * message being answered, and handing a reader one blob invites a figure from\n * that message to be read back as the sender's own.\n *\n * Splits at the earliest separator Outlook or the common webmail clients emit.\n * If that would leave no new text at all — a bottom-posted reply, or a body\n * that opens on a quote — the split is abandoned and everything is `body`,\n * since losing the sender's actual words is the one outcome worth avoiding.\n */\nexport function splitQuotedOriginal(text: string): { body: string; quoted: string; separator: string } {\n const lines = text.split(/\\r?\\n/);\n const whole = { body: text, quoted: '', separator: '' };\n for (let i = 0; i < lines.length; i++) {\n const line = lines[i].trim();\n let separator = '';\n if (/^-{2,}\\s*original message\\s*-{2,}$/i.test(line)) separator = '-----Original Message-----';\n // Outlook's HTML thread rule, directly above the From:/Sent: block.\n else if (/^_{10,}$/.test(line)) separator = 'Outlook thread rule';\n // \"On Tue, Jul 21, 2026 at 9:14 AM Jo Doe <jo@example.com> wrote:\"\n else if (/^on\\b.{5,300}\\bwrote:$/i.test(line)) separator = 'On … wrote:';\n else if (/^>/.test(lines[i])) separator = '> quoted lines';\n // A bare header block: \"From: …\" followed by another header line.\n else if (/^from:\\s*\\S/i.test(line) && /^(sent|date|to|subject|cc):/i.test((lines[i + 1] || '').trim())) {\n separator = 'From:/Sent: header block';\n }\n if (!separator) continue;\n const body = lines.slice(0, i).join('\\n').trimEnd();\n if (!body.trim()) return whole;\n return { body, quoted: lines.slice(i).join('\\n').trim(), separator };\n }\n return whole;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"mail.js","sourceRoot":"","sources":["../src/mail.ts"],"names":[],"mappings":"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","sourcesContent":["// Platform-neutral mail helpers: folder strings, reply prefixes, splitting a\n// reply from the thread it quotes, and what deleting mail reports. Pure\n// functions, usable without Outlook, and shared by both platforms so neither can\n// come to read these differently.\nimport { InvalidRequestError } from './errors';\nimport type { DeleteMailOutcome, DeleteMailResult, MailFolderRef } from './types';\n\n/**\n * Outlook's olDefaultFolders ids for the folders this package addresses by\n * role rather than by name. The ids are Outlook's own, and a folder's NAME is\n * localized, so a role is always resolved through its id.\n */\nexport const FolderId = Object.freeze({\n DeletedItems: 3,\n Outbox: 4,\n SentMail: 5,\n Inbox: 6,\n Drafts: 16,\n Junk: 23,\n});\n\n/**\n * The folders addressable by name rather than by walking from the Inbox, with\n * their olDefaultFolders id. A well-known name wins over a user folder of the\n * same name; reach the latter by qualifying it (`Inbox\\Drafts`).\n */\nexport const WELL_KNOWN_FOLDERS: Readonly<Record<string, number>> = Object.freeze({\n 'inbox': FolderId.Inbox,\n 'sent items': FolderId.SentMail,\n 'sent': FolderId.SentMail,\n 'sent mail': FolderId.SentMail,\n 'drafts': FolderId.Drafts,\n 'deleted items': FolderId.DeletedItems,\n 'deleted': FolderId.DeletedItems,\n 'trash': FolderId.DeletedItems,\n 'junk email': FolderId.Junk,\n 'junk': FolderId.Junk,\n 'outbox': FolderId.Outbox,\n});\n\n/** The Inbox root, as a folder reference. */\nconst INBOX_REF: Readonly<MailFolderRef> = Object.freeze({\n rootId: FolderId.Inbox,\n rootLabel: 'Inbox',\n segments: [],\n});\n\n/**\n * Roots whose contents are received or sent mail rather than working state —\n * what `deleteOutlookEmails` refuses to touch, subfolders included, unless\n * allowed. Matched by id on both platforms, as every role is.\n */\nexport const PROTECTED_ROOTS: readonly number[] = Object.freeze([FolderId.Inbox, FolderId.SentMail]);\n\n/**\n * Why `deleteOutlookEmails` refuses an item, worded once for both platforms —\n * it reaches the caller verbatim as a per-item `reason`.\n */\nexport const PROTECTED_MAIL_REASON =\n 'received or sent mail (Inbox/Sent Items or a subfolder) - pass allowProtected to override';\n\nconst DELETE_STATUSES: readonly DeleteMailOutcome['status'][] = ['deleted', 'would-delete', 'refused', 'failed'];\n\n/** The status a delete script reported for one item; anything it shouldn't have said is a failure. */\nexport function deleteStatus(reported: string): DeleteMailOutcome['status'] {\n return DELETE_STATUSES.find(status => status === reported) ?? 'failed';\n}\n\n/** A deleteOutlookEmails result: every outcome, and how many ended each way. */\nexport function deleteMailResult(dryRun: boolean, items: DeleteMailOutcome[]): DeleteMailResult {\n const tally = (status: DeleteMailOutcome['status']) => items.filter(item => item.status === status).length;\n return { dryRun, deleted: tally('deleted'), refused: tally('refused'), failed: tally('failed'), items };\n}\n\n/**\n * Roots whose items are outgoing mail, which carries a sent time where incoming\n * mail carries a received one — so a date filter has to know which it's on.\n */\nexport function isOutgoingRoot(rootId: number): boolean {\n return rootId === FolderId.SentMail || rootId === FolderId.Outbox || rootId === FolderId.Drafts;\n}\n\n/**\n * Roots an Inbox walk must never descend into.\n *\n * On an Exchange profile these sit beside the Inbox and a walk from it can't\n * reach them anyway. On an IMAP profile Outlook nests them UNDER the Inbox, so\n * the same walk would hand back mail already sent, drafted or thrown away as if\n * it had just arrived. They are matched by id, never by name: the names are\n * localized.\n */\nexport const NON_INCOMING_ROOTS: readonly number[] = Object.freeze([\n FolderId.DeletedItems,\n FolderId.Outbox,\n FolderId.SentMail,\n FolderId.Drafts,\n FolderId.Junk,\n]);\n\n/**\n * Subject prefixes that mark a reply or a forward.\n *\n * Kept as a pattern SOURCE because it runs in two engines: macOS tests it as a\n * JavaScript RegExp, Windows inside PowerShell's .NET one, and both read every\n * construct used here the same way. Non-ASCII is written as \\uXXXX so the\n * generated PowerShell stays plain ASCII.\n *\n * Not only English: mail threaded in Chinese, Japanese or Korean replies under\n * 回复:/返信:/답장: and forwards under 转发:/転送:/전달:, and the European\n * clients use AW:, WG:, SV:, TR:, RV:, ENC: and friends. The colon may be the\n * full-width one a CJK input method types, and Outlook's reply counter (RE[2]:)\n * can sit before it.\n */\nconst REPLY_PREFIXES: readonly string[] = [\n 're', 'fwd?', 'aw', 'wg', 'sv', 'vs', 'vb', 'tr', 'res', 'rv', 'enc', 'odp',\n '\\\\u56DE\\\\u590D', '\\\\u56DE\\\\u8986', '\\\\u7B54\\\\u590D', '\\\\u7B54\\\\u8986',\n '\\\\u8F6C\\\\u53D1', '\\\\u8F49\\\\u767C', '\\\\u8FD4\\\\u4FE1', '\\\\u8EE2\\\\u9001',\n '\\\\uB2F5\\\\uC7A5', '\\\\uC804\\\\uB2EC',\n];\n\n/** The reply/forward prefixes as one case-insensitive pattern source. */\nexport const REPLY_PREFIX_SOURCE =\n '^\\\\s*(?:' + REPLY_PREFIXES.join('|') + ')\\\\s*(?:\\\\[\\\\d+\\\\])?\\\\s*[:\\\\uFF1A]';\nexport const REPLY_PREFIX = new RegExp(REPLY_PREFIX_SOURCE, 'i');\n\n/**\n * Whether a subject opens on a reply or forward prefix.\n *\n * This reads what the subject claims, not how the email was made: a forward\n * carries a prefix too, and a client can leave one off a real reply.\n */\nexport function hasReplyPrefix(subject: string): boolean {\n return REPLY_PREFIX.test(subject);\n}\n\n/**\n * The subject every message in a thread shares: every reply and forward prefix\n * stripped, however many clients stacked them (`RE: FW: RE[2]: Rate request`\n * is `Rate request`), and whitespace collapsed. Case is kept, so lower-case\n * both sides to compare two threads.\n */\nexport function threadSubject(subject: string): string {\n let rest = subject;\n // Each pass removes at least the prefix's colon, so this always ends.\n while (REPLY_PREFIX.test(rest)) rest = rest.replace(REPLY_PREFIX, '');\n return rest.replace(/\\s+/g, ' ').trim();\n}\n\n/**\n * A subject glob as an anchored regex source: `*` matches any run, `?` one\n * character, everything else itself. Written with constructs JavaScript and\n * .NET read identically, so both platforms apply exactly the same test.\n */\nexport function subjectGlobSource(glob: string): string {\n let source = '';\n for (const ch of glob) {\n if (ch === '*') source += '.*';\n else if (ch === '?') source += '.';\n else source += ch.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n }\n return `^${source}$`;\n}\n\n/** The leaf name of a folder path or bare name. */\nexport function folderLeafName(entry: string): string {\n return entry.trim().split(/[\\\\/]/).filter(Boolean).pop() ?? '';\n}\n\n/** A full Outlook folder path starts with the mailbox: `\\\\mailbox@example.com\\…`. */\nconst MAILBOX_PREFIX = /^\\\\\\\\[^\\\\/]+[\\\\/]/;\n\nfunction folderSegments(folder: string): string[] {\n return folder.trim().replace(MAILBOX_PREFIX, '').split(/[\\\\/]+/).map(s => s.trim()).filter(Boolean);\n}\n\n/**\n * Resolve a folder string to a well-known root plus the segments below it.\n *\n * Accepts every shape a person has to hand: a bare name ('Invoices'), a\n * relative path ('Invoices\\Paid'), a well-known folder ('Sent Items'), a path\n * under one ('Deleted Items\\2026'), and the full path listInboxFolders prints\n * ('\\\\team@example.com\\Inbox\\Invoices'). Anything not starting at a well-known\n * folder is taken as relative to the Inbox.\n */\nexport function mailFolderRef(folder: string): MailFolderRef {\n const segments = folderSegments(folder);\n const head = segments[0]?.toLowerCase();\n if (head !== undefined && Object.prototype.hasOwnProperty.call(WELL_KNOWN_FOLDERS, head)) {\n return { rootId: WELL_KNOWN_FOLDERS[head], rootLabel: segments[0], segments: segments.slice(1) };\n }\n return { rootId: FolderId.Inbox, rootLabel: 'Inbox', segments };\n}\n\n/**\n * A caller's folder option as a reference: none given means the Inbox root,\n * and a string that names no folder at all (only separators) is refused rather\n * than silently read as the Inbox root.\n */\nexport function folderOption(folder: string | undefined): { ref: MailFolderRef; label: string } {\n if (folder === undefined || folder.trim() === '') return { ref: INBOX_REF, label: 'Inbox' };\n if (folderSegments(folder).length === 0) {\n throw new InvalidRequestError(`Folder '${folder}' does not name a folder.`);\n }\n return { ref: mailFolderRef(folder), label: folder.trim() };\n}\n\n/**\n * Split a plain-text reply into the sender's own text and the quoted thread\n * below it.\n *\n * This matters more than it looks: the quoted original carries the text of the\n * message being answered, and handing a reader one blob invites a figure from\n * that message to be read back as the sender's own.\n *\n * Splits at the earliest separator Outlook or the common webmail clients emit.\n * If that would leave no new text at all — a bottom-posted reply, or a body\n * that opens on a quote — the split is abandoned and everything is `body`,\n * since losing the sender's actual words is the one outcome worth avoiding.\n */\nexport function splitQuotedOriginal(text: string): { body: string; quoted: string; separator: string } {\n const lines = text.split(/\\r?\\n/);\n const whole = { body: text, quoted: '', separator: '' };\n for (let i = 0; i < lines.length; i++) {\n const line = lines[i].trim();\n let separator = '';\n if (/^-{2,}\\s*original message\\s*-{2,}$/i.test(line)) separator = '-----Original Message-----';\n // Outlook's HTML thread rule, directly above the From:/Sent: block.\n else if (/^_{10,}$/.test(line)) separator = 'Outlook thread rule';\n // \"On Tue, Jul 21, 2026 at 9:14 AM Jo Doe <jo@example.com> wrote:\"\n else if (/^on\\b.{5,300}\\bwrote:$/i.test(line)) separator = 'On … wrote:';\n else if (/^>/.test(lines[i])) separator = '> quoted lines';\n // A bare header block: \"From: …\" followed by another header line.\n else if (/^from:\\s*\\S/i.test(line) && /^(sent|date|to|subject|cc):/i.test((lines[i + 1] || '').trim())) {\n separator = 'From:/Sent: header block';\n }\n if (!separator) continue;\n const body = lines.slice(0, i).join('\\n').trimEnd();\n if (!body.trim()) return whole;\n return { body, quoted: lines.slice(i).join('\\n').trim(), separator };\n }\n return whole;\n}\n"]}
|
package/dist/types.d.ts
CHANGED
|
@@ -107,6 +107,15 @@ export interface ReplyEmailResult {
|
|
|
107
107
|
subject: string;
|
|
108
108
|
/** The original email's sender — check it is who you meant to answer. */
|
|
109
109
|
repliedToSender: string;
|
|
110
|
+
/**
|
|
111
|
+
* The draft's entry id when the reply was filed silently into Drafts
|
|
112
|
+
* (`openDraftWindow: false`), as `SendEmailResult.draftEntryId` reports a
|
|
113
|
+
* new email's: the id `listOutlookDrafts` gives it, so a caller can later
|
|
114
|
+
* send or delete exactly the replies it drafted. '' when the reply was
|
|
115
|
+
* sent or opened in a compose window, and on macOS when Drafts gained
|
|
116
|
+
* another draft with the same subject in the same moment.
|
|
117
|
+
*/
|
|
118
|
+
draftEntryId: string;
|
|
110
119
|
}
|
|
111
120
|
/** One item a batch operation could not handle, and why. */
|
|
112
121
|
export interface ItemFailure {
|
|
@@ -320,8 +329,8 @@ export interface InboxSearchFilter {
|
|
|
320
329
|
/**
|
|
321
330
|
* Folders under the Inbox to leave out, by full folder path or bare name,
|
|
322
331
|
* case-insensitively. An excluded folder takes its whole subtree with it.
|
|
323
|
-
* Sent, Drafts, Deleted and
|
|
324
|
-
* nests them under the Inbox. macOS matches the leaf name only.
|
|
332
|
+
* Sent, Drafts, Deleted, Junk and the Outbox are always skipped, even where
|
|
333
|
+
* the profile nests them under the Inbox. macOS matches the leaf name only.
|
|
325
334
|
*/
|
|
326
335
|
excludeFolders?: readonly string[];
|
|
327
336
|
/**
|
|
@@ -548,7 +557,11 @@ export interface OutlookBridge {
|
|
|
548
557
|
locateEmail(description: EmailDescription, options?: LocateEmailOptions): Promise<InboxSearchMatch | null>;
|
|
549
558
|
/** The folders under an account's Inbox. */
|
|
550
559
|
listInboxFolders(emailAccount: string, options?: ListFoldersOptions): Promise<InboxFolderInfo[]>;
|
|
551
|
-
/**
|
|
560
|
+
/**
|
|
561
|
+
* File emails into a folder. Moving rewrites each email's entry id. Only the
|
|
562
|
+
* account's own mail is moved: an id that resolves in another mailbox fails
|
|
563
|
+
* as that item.
|
|
564
|
+
*/
|
|
552
565
|
moveOutlookEmails(emailAccount: string, entryIds: readonly string[], folder: string, options?: MoveEmailsOptions): Promise<MoveEmailsResult>;
|
|
553
566
|
/** The mail drafts bound to an account, newest first. */
|
|
554
567
|
listOutlookDrafts(emailAccount: string, options?: ListDraftsOptions): Promise<ListDraftsResult>;
|
|
@@ -558,7 +571,10 @@ export interface OutlookBridge {
|
|
|
558
571
|
sendAllDrafts(emailAccount: string): Promise<SendDraftsResult>;
|
|
559
572
|
/** Delete the named drafts (into Deleted Items) — each has to be one of this account's drafts. */
|
|
560
573
|
deleteOutlookDrafts(emailAccount: string, entryIds: readonly string[]): Promise<DeleteDraftsResult>;
|
|
561
|
-
/**
|
|
574
|
+
/**
|
|
575
|
+
* Delete mail by id into Deleted Items, refusing received and sent mail
|
|
576
|
+
* unless allowed — and mail in any other mailbox whatever is allowed.
|
|
577
|
+
*/
|
|
562
578
|
deleteOutlookEmails(emailAccount: string, entryIds: readonly string[], options?: DeleteMailOptions): Promise<DeleteMailResult>;
|
|
563
579
|
/** Permanently empty an account's Deleted Items. The one irreversible operation. */
|
|
564
580
|
purgeDeletedItems(emailAccount: string, options?: PurgeDeletedItemsOptions): Promise<PurgeDeletedItemsResult>;
|