@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/README.md
CHANGED
|
@@ -7,7 +7,10 @@ cloud permissions: this automates the desktop client itself, the way a person wo
|
|
|
7
7
|
- **Windows** — PowerShell and Outlook's COM object model.
|
|
8
8
|
- **macOS** — AppleScript. Requires **legacy** Outlook for Mac: New Outlook implements only a slice of the AppleScript
|
|
9
9
|
dictionary (accounts don't enumerate, the inbox reports no messages), so every call fails with `ACCOUNT_NOT_FOUND`
|
|
10
|
-
there rather than quietly doing the wrong thing.
|
|
10
|
+
there rather than quietly doing the wrong thing. A Microsoft 365 mailbox Outlook syncs through Microsoft's cloud is
|
|
11
|
+
listed only in Outlook's profile, which macOS lets an app read once it has **Full Disk Access** — the app running
|
|
12
|
+
this package, or the terminal or editor it was started from. Without it, calls about such a mailbox fail with
|
|
13
|
+
`PERMISSION_DENIED`, naming the app to turn on.
|
|
11
14
|
- **Anywhere else** — `capabilities()` reports nothing supported and every call rejects with `UNSUPPORTED_PLATFORM`.
|
|
12
15
|
|
|
13
16
|
## Install
|
|
@@ -50,7 +53,7 @@ same defaults and the same validation on both platforms.
|
|
|
50
53
|
| Accounts | `getOutlookAccounts` |
|
|
51
54
|
| Reading | `readInboxEmails` (the Inbox root, or any folder by name or path), `searchInboxByFilter` (walks the whole Inbox tree by subject glob or pattern, date window, attachments, folders), `readEmailBody` (the sender's own text, split from the quoted thread), `readSelectedEmail`, `openOutlookEmail`, `locateEmail` (finds an email again by subject, sender and time once its id has gone stale) |
|
|
52
55
|
| Folders | `listInboxFolders`, `moveOutlookEmails` (creates a missing destination chain on request) |
|
|
53
|
-
| Composing | `sendOutlookEmail` (to/cc/bcc, several attachments, a named signature, send or draft; a draft filed without a window returns its id, so a caller can later send exactly the drafts it made), `replyOutlookEmail` (reply or reply-all, from HTML or a saved template) |
|
|
56
|
+
| Composing | `sendOutlookEmail` (to/cc/bcc, several attachments, a named signature, send or draft; a draft filed without a window returns its id, so a caller can later send exactly the drafts it made), `replyOutlookEmail` (reply or reply-all, from HTML or a saved template; a reply filed without a window returns its draft's id too) |
|
|
54
57
|
| Drafts | `listOutlookDrafts`, `sendDrafts` (a chosen subset), `sendAllDrafts`, `deleteOutlookDrafts` |
|
|
55
58
|
| Attachments | `saveEmailAttachment`, `saveEmailAttachments` |
|
|
56
59
|
| Cleanup | `deleteOutlookEmails` (refuses received and sent mail unless allowed; dry-run first), `purgeDeletedItems` |
|
|
@@ -71,10 +74,11 @@ to keep a `storeId` with its own `entryId`; the store id is what lets an email r
|
|
|
71
74
|
default one.
|
|
72
75
|
|
|
73
76
|
**Ids are not portable between platforms.** Windows returns MAPI EntryIDs; macOS returns Outlook for macOS small integer
|
|
74
|
-
message ids, and has no store id (it reports `''` and ignores one passed in).
|
|
75
|
-
`INVALID_REQUEST
|
|
76
|
-
|
|
77
|
-
|
|
77
|
+
message ids, and has no store id (it reports `''` and ignores one passed in). macOS refuses a Windows EntryID outright,
|
|
78
|
+
with `INVALID_REQUEST`; on Windows a macOS id simply resolves to nothing and fails as `NOT_FOUND`. A batch reports
|
|
79
|
+
either as that item's failure. **Moving an email rewrites its id** on both, so ids saved before a move are stale
|
|
80
|
+
afterward. Keep the subject, sender and received time beside a saved id, and `locateEmail` finds the email again from
|
|
81
|
+
them wherever it has moved under the Inbox, on either platform.
|
|
78
82
|
|
|
79
83
|
Timestamps are local time as `yyyy-MM-dd HH:mm`, which sorts correctly as a string.
|
|
80
84
|
|
|
@@ -170,6 +174,7 @@ try {
|
|
|
170
174
|
| `ACCOUNT_NOT_FOUND` | `AccountNotFoundError` | No mailbox in the profile matches the address. Carries `account`. |
|
|
171
175
|
| `NOT_FOUND` | `NotFoundError` | A `folder`, `email`, `attachment`, `template`, `signature` or `file` didn't resolve. Carries `kind`. |
|
|
172
176
|
| `INVALID_REQUEST` | `InvalidRequestError` | The arguments can't produce a call — a wrong type, a missing value, a template edited into an unusable state, or a mailbox that can be read but not sent from. |
|
|
177
|
+
| `PERMISSION_DENIED` | `PermissionDeniedError` | macOS refused Outlook's profile, so a mailbox only it lists could not be seen. The message names the app to give Full Disk Access. |
|
|
173
178
|
| `SCRIPT_FAILED` | `ScriptError` | The script ran and failed. Carries `script`, `line`, `stderr`, `runner`, `durationMs`. |
|
|
174
179
|
| `OUTPUT_TOO_LARGE` | `OutputTooLargeError` | The script printed more than `maxBufferBytes`. |
|
|
175
180
|
| `TIMEOUT` | `TimeoutError` | The run exceeded its time budget and was killed. Carries `timeoutMs`. |
|
package/dist/errors.d.ts
CHANGED
|
@@ -6,12 +6,13 @@
|
|
|
6
6
|
* - `ACCOUNT_NOT_FOUND` — no mailbox in the Outlook profile matches the address.
|
|
7
7
|
* - `NOT_FOUND` — a folder, email, attachment, template, signature or file didn't resolve.
|
|
8
8
|
* - `INVALID_REQUEST` — the arguments can't produce a call.
|
|
9
|
+
* - `PERMISSION_DENIED` — macOS privacy control kept the package from what it needed.
|
|
9
10
|
* - `SCRIPT_FAILED` — the automation script ran and failed.
|
|
10
11
|
* - `OUTPUT_TOO_LARGE` — the script printed more than `maxBufferBytes` and was killed.
|
|
11
12
|
* - `TIMEOUT` — the run exceeded its time budget and was killed.
|
|
12
13
|
* - `ABORTED` — the caller's AbortSignal fired.
|
|
13
14
|
*/
|
|
14
|
-
export type OutlookErrorCode = 'UNSUPPORTED_PLATFORM' | 'NOT_IMPLEMENTED' | 'ACCOUNT_NOT_FOUND' | 'NOT_FOUND' | 'INVALID_REQUEST' | 'SCRIPT_FAILED' | 'OUTPUT_TOO_LARGE' | 'TIMEOUT' | 'ABORTED';
|
|
15
|
+
export type OutlookErrorCode = 'UNSUPPORTED_PLATFORM' | 'NOT_IMPLEMENTED' | 'ACCOUNT_NOT_FOUND' | 'NOT_FOUND' | 'INVALID_REQUEST' | 'PERMISSION_DENIED' | 'SCRIPT_FAILED' | 'OUTPUT_TOO_LARGE' | 'TIMEOUT' | 'ABORTED';
|
|
15
16
|
/** The interpreter a script ran under. */
|
|
16
17
|
export type ScriptRunner = 'powershell' | 'osascript';
|
|
17
18
|
/** What a NOT_FOUND error failed to find. */
|
|
@@ -19,7 +20,11 @@ export type NotFoundKind = 'folder' | 'email' | 'attachment' | 'template' | 'sig
|
|
|
19
20
|
/** Base class for every error this package raises deliberately. */
|
|
20
21
|
export declare class OutlookError extends Error {
|
|
21
22
|
readonly code: OutlookErrorCode;
|
|
22
|
-
/**
|
|
23
|
+
/**
|
|
24
|
+
* The underlying failure, when there was one. Declared rather than defined:
|
|
25
|
+
* a class field would give every error a `cause` of undefined, and an error
|
|
26
|
+
* with no cause should not have the property at all.
|
|
27
|
+
*/
|
|
23
28
|
readonly cause?: unknown;
|
|
24
29
|
constructor(code: OutlookErrorCode, message: string, options?: {
|
|
25
30
|
cause?: unknown;
|
|
@@ -52,6 +57,14 @@ export declare class NotFoundError extends OutlookError {
|
|
|
52
57
|
export declare class InvalidRequestError extends OutlookError {
|
|
53
58
|
constructor(message: string);
|
|
54
59
|
}
|
|
60
|
+
/**
|
|
61
|
+
* macOS privacy control kept the package from what it needed: on a Mac, Outlook's
|
|
62
|
+
* profile, which is the only place a Microsoft 365 mailbox synced through
|
|
63
|
+
* Microsoft's cloud is listed. The message names the app to grant access to.
|
|
64
|
+
*/
|
|
65
|
+
export declare class PermissionDeniedError extends OutlookError {
|
|
66
|
+
constructor(message: string);
|
|
67
|
+
}
|
|
55
68
|
/**
|
|
56
69
|
* The script ran and failed.
|
|
57
70
|
*
|
|
@@ -64,7 +77,7 @@ export declare class ScriptError extends OutlookError {
|
|
|
64
77
|
readonly script: string;
|
|
65
78
|
readonly stderr: string;
|
|
66
79
|
readonly durationMs: number;
|
|
67
|
-
/** The line of `script` the failure was raised on, when the runner reports one. */
|
|
80
|
+
/** The line of `script` the failure was raised on, when the runner reports one; absent otherwise, as `cause` is. */
|
|
68
81
|
readonly line?: number;
|
|
69
82
|
constructor(init: {
|
|
70
83
|
runner: ScriptRunner;
|
|
@@ -103,14 +116,12 @@ export declare class AbortedError extends OutlookError {
|
|
|
103
116
|
readonly runner: ScriptRunner;
|
|
104
117
|
constructor(runner: ScriptRunner, reason?: unknown);
|
|
105
118
|
}
|
|
106
|
-
/** The codes a script may raise by tagging its failure message. */
|
|
107
|
-
export type ScriptFailureCode = 'ACCOUNT_NOT_FOUND' | 'NOT_FOUND' | 'INVALID_REQUEST';
|
|
108
119
|
/**
|
|
109
120
|
* The tag that opens a typed script failure, ready to be followed by the
|
|
110
121
|
* human-readable message. Plain ASCII with no quote or `$`, so it can sit
|
|
111
122
|
* inside a PowerShell or AppleScript string literal unescaped.
|
|
112
123
|
*/
|
|
113
|
-
export declare function failureTag(code: 'ACCOUNT_NOT_FOUND' | 'INVALID_REQUEST'): string;
|
|
124
|
+
export declare function failureTag(code: 'ACCOUNT_NOT_FOUND' | 'INVALID_REQUEST' | 'PERMISSION_DENIED'): string;
|
|
114
125
|
export declare function failureTag(code: 'NOT_FOUND', kind: NotFoundKind): string;
|
|
115
126
|
/**
|
|
116
127
|
* The typed error a tagged script message stands for, or null when the
|
package/dist/errors.js
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
// reworded without breaking anyone. `err instanceof OutlookError` alone
|
|
8
8
|
// separates an expected failure from a bug.
|
|
9
9
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
|
-
exports.AbortedError = exports.TimeoutError = exports.OutputTooLargeError = exports.ScriptError = exports.InvalidRequestError = exports.NotFoundError = exports.AccountNotFoundError = exports.NotImplementedError = exports.UnsupportedPlatformError = exports.OutlookError = void 0;
|
|
10
|
+
exports.AbortedError = exports.TimeoutError = exports.OutputTooLargeError = exports.ScriptError = exports.PermissionDeniedError = exports.InvalidRequestError = exports.NotFoundError = exports.AccountNotFoundError = exports.NotImplementedError = exports.UnsupportedPlatformError = exports.OutlookError = void 0;
|
|
11
11
|
exports.failureTag = failureTag;
|
|
12
12
|
exports.errorFromTaggedMessage = errorFromTaggedMessage;
|
|
13
13
|
exports.classifyRunFailure = classifyRunFailure;
|
|
@@ -15,14 +15,12 @@ const NOT_FOUND_KINDS = ['folder', 'email', 'attachment', 'template', 'signature
|
|
|
15
15
|
/** Base class for every error this package raises deliberately. */
|
|
16
16
|
class OutlookError extends Error {
|
|
17
17
|
code;
|
|
18
|
-
/** The underlying failure, when there was one. */
|
|
19
|
-
cause;
|
|
20
18
|
constructor(code, message, options) {
|
|
21
19
|
// `cause` is an own enumerable field rather than `super`'s option: that
|
|
22
20
|
// is what puts it in a JSON dump of the error, which is where someone
|
|
23
21
|
// logging a failure looks for it.
|
|
24
22
|
super(message);
|
|
25
|
-
if (options
|
|
23
|
+
if (options?.cause !== undefined)
|
|
26
24
|
this.cause = options.cause;
|
|
27
25
|
this.code = code;
|
|
28
26
|
// `name` is what an unhandled-rejection dump shows, so make it the subclass.
|
|
@@ -77,6 +75,17 @@ class InvalidRequestError extends OutlookError {
|
|
|
77
75
|
}
|
|
78
76
|
}
|
|
79
77
|
exports.InvalidRequestError = InvalidRequestError;
|
|
78
|
+
/**
|
|
79
|
+
* macOS privacy control kept the package from what it needed: on a Mac, Outlook's
|
|
80
|
+
* profile, which is the only place a Microsoft 365 mailbox synced through
|
|
81
|
+
* Microsoft's cloud is listed. The message names the app to grant access to.
|
|
82
|
+
*/
|
|
83
|
+
class PermissionDeniedError extends OutlookError {
|
|
84
|
+
constructor(message) {
|
|
85
|
+
super('PERMISSION_DENIED', message);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
exports.PermissionDeniedError = PermissionDeniedError;
|
|
80
89
|
/**
|
|
81
90
|
* The script ran and failed.
|
|
82
91
|
*
|
|
@@ -89,8 +98,6 @@ class ScriptError extends OutlookError {
|
|
|
89
98
|
script;
|
|
90
99
|
stderr;
|
|
91
100
|
durationMs;
|
|
92
|
-
/** The line of `script` the failure was raised on, when the runner reports one. */
|
|
93
|
-
line;
|
|
94
101
|
constructor(init) {
|
|
95
102
|
super('SCRIPT_FAILED', init.message || init.stderr || `${init.runner} failed with no output.`, { cause: init.cause });
|
|
96
103
|
this.runner = init.runner;
|
|
@@ -141,7 +148,7 @@ exports.AbortedError = AbortedError;
|
|
|
141
148
|
function failureTag(code, kind) {
|
|
142
149
|
return `[outlook-bridge:${code}${kind ? `:${kind}` : ''}] `;
|
|
143
150
|
}
|
|
144
|
-
const FAILURE_TAG = /\[outlook-bridge:(ACCOUNT_NOT_FOUND|NOT_FOUND|INVALID_REQUEST)(?::([a-z]+))?\]\s*/;
|
|
151
|
+
const FAILURE_TAG = /\[outlook-bridge:(ACCOUNT_NOT_FOUND|NOT_FOUND|INVALID_REQUEST|PERMISSION_DENIED)(?::([a-z]+))?\]\s*/;
|
|
145
152
|
/** A script's account-not-found message names the address in single quotes. */
|
|
146
153
|
const QUOTED_ACCOUNT = /'(.*)'/;
|
|
147
154
|
/**
|
|
@@ -156,6 +163,8 @@ function errorFromTaggedMessage(message) {
|
|
|
156
163
|
switch (match[1]) {
|
|
157
164
|
case 'ACCOUNT_NOT_FOUND':
|
|
158
165
|
return new AccountNotFoundError(QUOTED_ACCOUNT.exec(text)?.[1] ?? '', text);
|
|
166
|
+
case 'PERMISSION_DENIED':
|
|
167
|
+
return new PermissionDeniedError(text);
|
|
159
168
|
case 'NOT_FOUND': {
|
|
160
169
|
const kind = NOT_FOUND_KINDS.find(k => k === match[2]) ?? 'email';
|
|
161
170
|
return new NotFoundError(kind, text);
|
package/dist/errors.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"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","sourcesContent":["// The package's error taxonomy.\n//\n// Every failure the package raises on purpose is an `OutlookError` carrying a\n// stable `code`. The codes are the API; the messages beside them are not — a\n// caller branches on `error.code`, never on message text, so a message can be\n// reworded without breaking anyone. `err instanceof OutlookError` alone\n// separates an expected failure from a bug.\n\n/**\n * Stable discriminants.\n *\n * - `UNSUPPORTED_PLATFORM` — no Outlook automation exists on this OS.\n * - `NOT_IMPLEMENTED` — the platform could do this, but this corner isn't written.\n * - `ACCOUNT_NOT_FOUND` — no mailbox in the Outlook profile matches the address.\n * - `NOT_FOUND` — a folder, email, attachment, template, signature or file didn't resolve.\n * - `INVALID_REQUEST` — the arguments can't produce a call.\n * - `SCRIPT_FAILED` — the automation script ran and failed.\n * - `OUTPUT_TOO_LARGE` — the script printed more than `maxBufferBytes` and was killed.\n * - `TIMEOUT` — the run exceeded its time budget and was killed.\n * - `ABORTED` — the caller's AbortSignal fired.\n */\nexport type OutlookErrorCode =\n | 'UNSUPPORTED_PLATFORM'\n | 'NOT_IMPLEMENTED'\n | 'ACCOUNT_NOT_FOUND'\n | 'NOT_FOUND'\n | 'INVALID_REQUEST'\n | 'SCRIPT_FAILED'\n | 'OUTPUT_TOO_LARGE'\n | 'TIMEOUT'\n | 'ABORTED';\n\n/** The interpreter a script ran under. */\nexport type ScriptRunner = 'powershell' | 'osascript';\n\n/** What a NOT_FOUND error failed to find. */\nexport type NotFoundKind = 'folder' | 'email' | 'attachment' | 'template' | 'signature' | 'file';\n\nconst NOT_FOUND_KINDS: readonly NotFoundKind[] = ['folder', 'email', 'attachment', 'template', 'signature', 'file'];\n\n/** Base class for every error this package raises deliberately. */\nexport class OutlookError extends Error {\n readonly code: OutlookErrorCode;\n\n /** The underlying failure, when there was one. */\n readonly cause?: unknown;\n\n constructor(code: OutlookErrorCode, message: string, options?: { cause?: unknown }) {\n // `cause` is an own enumerable field rather than `super`'s option: that\n // is what puts it in a JSON dump of the error, which is where someone\n // logging a failure looks for it.\n super(message);\n if (options && 'cause' in options) this.cause = options.cause;\n this.code = code;\n // `name` is what an unhandled-rejection dump shows, so make it the subclass.\n this.name = new.target.name;\n }\n}\n\n/** This OS has no Outlook automation. */\nexport class UnsupportedPlatformError extends OutlookError {\n /** The platform that was asked, i.e. `process.platform` at the time. */\n readonly platform: string;\n\n constructor(platform: string) {\n super('UNSUPPORTED_PLATFORM', `Outlook automation is only supported on Windows and macOS (running on '${platform}').`);\n this.platform = platform;\n }\n}\n\n/** The platform could do this, but this corner isn't written. */\nexport class NotImplementedError extends OutlookError {\n /** What isn't implemented. */\n readonly operation: string;\n readonly platform: string;\n\n constructor(operation: string, platform: string) {\n super('NOT_IMPLEMENTED', `${operation} is not implemented for Outlook on ${platform}.`);\n this.operation = operation;\n this.platform = platform;\n }\n}\n\n/** No mailbox in the Outlook profile matches the requested address. */\nexport class AccountNotFoundError extends OutlookError {\n readonly account: string;\n\n constructor(account: string, message = `Outlook account '${account}' not found.`) {\n super('ACCOUNT_NOT_FOUND', message);\n this.account = account;\n }\n}\n\n/** A named folder, email, attachment, template, signature or file didn't resolve. */\nexport class NotFoundError extends OutlookError {\n readonly kind: NotFoundKind;\n\n constructor(kind: NotFoundKind, message: string) {\n super('NOT_FOUND', message);\n this.kind = kind;\n }\n}\n\n/** The arguments can't produce a call. */\nexport class InvalidRequestError extends OutlookError {\n constructor(message: string) {\n super('INVALID_REQUEST', message);\n }\n}\n\n/**\n * The script ran and failed.\n *\n * Carries the generated script verbatim — the most useful thing to have when\n * one of these fires, and otherwise reachable only through a debug hook wired\n * up in advance.\n */\nexport class ScriptError extends OutlookError {\n readonly runner: ScriptRunner;\n readonly script: string;\n readonly stderr: string;\n readonly durationMs: number;\n /** The line of `script` the failure was raised on, when the runner reports one. */\n readonly line?: number;\n\n constructor(init: {\n runner: ScriptRunner;\n script: string;\n stderr: string;\n durationMs: number;\n message?: string;\n line?: number;\n cause?: unknown;\n }) {\n super('SCRIPT_FAILED', init.message || init.stderr || `${init.runner} failed with no output.`, { cause: init.cause });\n this.runner = init.runner;\n this.script = init.script;\n this.stderr = init.stderr;\n this.durationMs = init.durationMs;\n if (init.line !== undefined) this.line = init.line;\n }\n}\n\n/** The script printed more than `maxBufferBytes` and was killed. */\nexport class OutputTooLargeError extends OutlookError {\n readonly runner: ScriptRunner;\n readonly maxBufferBytes: number;\n readonly script: string;\n\n constructor(init: { runner: ScriptRunner; maxBufferBytes: number; script: string }) {\n super(\n 'OUTPUT_TOO_LARGE',\n `${init.runner} output exceeded maxBufferBytes (${init.maxBufferBytes}). `\n + 'Raise maxBufferBytes, or narrow the request.',\n );\n this.runner = init.runner;\n this.maxBufferBytes = init.maxBufferBytes;\n this.script = init.script;\n }\n}\n\n/** The run exceeded its time budget and was killed. */\nexport class TimeoutError extends OutlookError {\n readonly runner: ScriptRunner;\n readonly timeoutMs: number;\n readonly script: string;\n\n constructor(init: { runner: ScriptRunner; timeoutMs: number; script: string }) {\n super('TIMEOUT', `${init.runner} run exceeded its ${init.timeoutMs}ms budget and was killed.`);\n this.runner = init.runner;\n this.timeoutMs = init.timeoutMs;\n this.script = init.script;\n }\n}\n\n/** The caller's AbortSignal fired. */\nexport class AbortedError extends OutlookError {\n readonly runner: ScriptRunner;\n\n constructor(runner: ScriptRunner, reason?: unknown) {\n super('ABORTED', `${runner} run was aborted by the caller.`, { cause: reason });\n this.runner = runner;\n }\n}\n\n// ── Typed failures from inside a script ─────────────────────────────────\n//\n// A generated script can only fail by printing a message, so a script that\n// knows WHY it failed — the folder isn't there, the id resolves to nothing —\n// says so by starting the message with a tag: `[outlook-bridge:NOT_FOUND:folder]`.\n// The runner turns a tagged message into the matching error class and strips\n// the tag, so a caller can branch on NOT_FOUND without reading the sentence.\n\n/** The codes a script may raise by tagging its failure message. */\nexport type ScriptFailureCode = 'ACCOUNT_NOT_FOUND' | 'NOT_FOUND' | 'INVALID_REQUEST';\n\n/**\n * The tag that opens a typed script failure, ready to be followed by the\n * human-readable message. Plain ASCII with no quote or `$`, so it can sit\n * inside a PowerShell or AppleScript string literal unescaped.\n */\nexport function failureTag(code: 'ACCOUNT_NOT_FOUND' | 'INVALID_REQUEST'): string;\nexport function failureTag(code: 'NOT_FOUND', kind: NotFoundKind): string;\nexport function failureTag(code: ScriptFailureCode, kind?: NotFoundKind): string {\n return `[outlook-bridge:${code}${kind ? `:${kind}` : ''}] `;\n}\n\nconst FAILURE_TAG = /\\[outlook-bridge:(ACCOUNT_NOT_FOUND|NOT_FOUND|INVALID_REQUEST)(?::([a-z]+))?\\]\\s*/;\n\n/** A script's account-not-found message names the address in single quotes. */\nconst QUOTED_ACCOUNT = /'(.*)'/;\n\n/**\n * The typed error a tagged script message stands for, or null when the\n * message carries no tag.\n */\nexport function errorFromTaggedMessage(message: string): OutlookError | null {\n const match = FAILURE_TAG.exec(message);\n if (!match) return null;\n const text = (message.slice(0, match.index) + message.slice(match.index + match[0].length)).trim();\n switch (match[1]) {\n case 'ACCOUNT_NOT_FOUND':\n return new AccountNotFoundError(QUOTED_ACCOUNT.exec(text)?.[1] ?? '', text);\n case 'NOT_FOUND': {\n const kind = NOT_FOUND_KINDS.find(k => k === match[2]) ?? 'email';\n return new NotFoundError(kind, text);\n }\n default:\n return new InvalidRequestError(text);\n }\n}\n\n/**\n * Turn a failed run into the most specific error that can be proven.\n *\n * Order matters. An abort and a timeout both kill the child, so the signal's\n * own state is checked before the kill; an overflow kills it too, and is told\n * apart by its error code. Only then is the script's message worth reading — a\n * process killed mid-write may have printed a partial, misleading one.\n */\nexport function classifyRunFailure(init: {\n runner: ScriptRunner;\n script: string;\n /** Everything the run wrote to stderr. */\n stderr: string;\n /** The script's own failure message, when the runner could isolate one. */\n message?: string;\n /** The script line the failure was raised on, when known. */\n line?: number;\n durationMs: number;\n /** The error `execFile` handed back. */\n nodeError?: (Error & { killed?: boolean; code?: string | number | null }) | null;\n /** The time budget in force, for the TIMEOUT message. */\n timeoutMs?: number;\n /** The stdout cap in force, for the OUTPUT_TOO_LARGE message. */\n maxBufferBytes?: number;\n signal?: AbortSignal;\n}): OutlookError {\n const { runner, script, stderr, durationMs, nodeError, timeoutMs, signal } = init;\n\n if (signal?.aborted || nodeError?.name === 'AbortError' || nodeError?.code === 'ABORT_ERR') {\n return new AbortedError(runner, signal?.reason);\n }\n if (nodeError?.code === 'ERR_CHILD_PROCESS_STDIO_MAXBUFFER') {\n return new OutputTooLargeError({ runner, maxBufferBytes: init.maxBufferBytes ?? 0, script });\n }\n if (nodeError?.killed && timeoutMs) {\n return new TimeoutError({ runner, timeoutMs, script });\n }\n const message = (init.message ?? '').trim() || stderr.trim() || nodeError?.message || '';\n const typed = errorFromTaggedMessage(message);\n if (typed) return typed;\n return new ScriptError({\n runner,\n script,\n stderr,\n durationMs,\n message,\n line: init.line,\n cause: nodeError ?? undefined,\n });\n}\n"]}
|
|
1
|
+
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"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","sourcesContent":["// The package's error taxonomy.\n//\n// Every failure the package raises on purpose is an `OutlookError` carrying a\n// stable `code`. The codes are the API; the messages beside them are not — a\n// caller branches on `error.code`, never on message text, so a message can be\n// reworded without breaking anyone. `err instanceof OutlookError` alone\n// separates an expected failure from a bug.\n\n/**\n * Stable discriminants.\n *\n * - `UNSUPPORTED_PLATFORM` — no Outlook automation exists on this OS.\n * - `NOT_IMPLEMENTED` — the platform could do this, but this corner isn't written.\n * - `ACCOUNT_NOT_FOUND` — no mailbox in the Outlook profile matches the address.\n * - `NOT_FOUND` — a folder, email, attachment, template, signature or file didn't resolve.\n * - `INVALID_REQUEST` — the arguments can't produce a call.\n * - `PERMISSION_DENIED` — macOS privacy control kept the package from what it needed.\n * - `SCRIPT_FAILED` — the automation script ran and failed.\n * - `OUTPUT_TOO_LARGE` — the script printed more than `maxBufferBytes` and was killed.\n * - `TIMEOUT` — the run exceeded its time budget and was killed.\n * - `ABORTED` — the caller's AbortSignal fired.\n */\nexport type OutlookErrorCode =\n | 'UNSUPPORTED_PLATFORM'\n | 'NOT_IMPLEMENTED'\n | 'ACCOUNT_NOT_FOUND'\n | 'NOT_FOUND'\n | 'INVALID_REQUEST'\n | 'PERMISSION_DENIED'\n | 'SCRIPT_FAILED'\n | 'OUTPUT_TOO_LARGE'\n | 'TIMEOUT'\n | 'ABORTED';\n\n/** The interpreter a script ran under. */\nexport type ScriptRunner = 'powershell' | 'osascript';\n\n/** What a NOT_FOUND error failed to find. */\nexport type NotFoundKind = 'folder' | 'email' | 'attachment' | 'template' | 'signature' | 'file';\n\nconst NOT_FOUND_KINDS: readonly NotFoundKind[] = ['folder', 'email', 'attachment', 'template', 'signature', 'file'];\n\n/** Base class for every error this package raises deliberately. */\nexport class OutlookError extends Error {\n readonly code: OutlookErrorCode;\n\n /**\n * The underlying failure, when there was one. Declared rather than defined:\n * a class field would give every error a `cause` of undefined, and an error\n * with no cause should not have the property at all.\n */\n declare readonly cause?: unknown;\n\n constructor(code: OutlookErrorCode, message: string, options?: { cause?: unknown }) {\n // `cause` is an own enumerable field rather than `super`'s option: that\n // is what puts it in a JSON dump of the error, which is where someone\n // logging a failure looks for it.\n super(message);\n if (options?.cause !== undefined) this.cause = options.cause;\n this.code = code;\n // `name` is what an unhandled-rejection dump shows, so make it the subclass.\n this.name = new.target.name;\n }\n}\n\n/** This OS has no Outlook automation. */\nexport class UnsupportedPlatformError extends OutlookError {\n /** The platform that was asked, i.e. `process.platform` at the time. */\n readonly platform: string;\n\n constructor(platform: string) {\n super('UNSUPPORTED_PLATFORM', `Outlook automation is only supported on Windows and macOS (running on '${platform}').`);\n this.platform = platform;\n }\n}\n\n/** The platform could do this, but this corner isn't written. */\nexport class NotImplementedError extends OutlookError {\n /** What isn't implemented. */\n readonly operation: string;\n readonly platform: string;\n\n constructor(operation: string, platform: string) {\n super('NOT_IMPLEMENTED', `${operation} is not implemented for Outlook on ${platform}.`);\n this.operation = operation;\n this.platform = platform;\n }\n}\n\n/** No mailbox in the Outlook profile matches the requested address. */\nexport class AccountNotFoundError extends OutlookError {\n readonly account: string;\n\n constructor(account: string, message = `Outlook account '${account}' not found.`) {\n super('ACCOUNT_NOT_FOUND', message);\n this.account = account;\n }\n}\n\n/** A named folder, email, attachment, template, signature or file didn't resolve. */\nexport class NotFoundError extends OutlookError {\n readonly kind: NotFoundKind;\n\n constructor(kind: NotFoundKind, message: string) {\n super('NOT_FOUND', message);\n this.kind = kind;\n }\n}\n\n/** The arguments can't produce a call. */\nexport class InvalidRequestError extends OutlookError {\n constructor(message: string) {\n super('INVALID_REQUEST', message);\n }\n}\n\n/**\n * macOS privacy control kept the package from what it needed: on a Mac, Outlook's\n * profile, which is the only place a Microsoft 365 mailbox synced through\n * Microsoft's cloud is listed. The message names the app to grant access to.\n */\nexport class PermissionDeniedError extends OutlookError {\n constructor(message: string) {\n super('PERMISSION_DENIED', message);\n }\n}\n\n/**\n * The script ran and failed.\n *\n * Carries the generated script verbatim — the most useful thing to have when\n * one of these fires, and otherwise reachable only through a debug hook wired\n * up in advance.\n */\nexport class ScriptError extends OutlookError {\n readonly runner: ScriptRunner;\n readonly script: string;\n readonly stderr: string;\n readonly durationMs: number;\n /** The line of `script` the failure was raised on, when the runner reports one; absent otherwise, as `cause` is. */\n declare readonly line?: number;\n\n constructor(init: {\n runner: ScriptRunner;\n script: string;\n stderr: string;\n durationMs: number;\n message?: string;\n line?: number;\n cause?: unknown;\n }) {\n super('SCRIPT_FAILED', init.message || init.stderr || `${init.runner} failed with no output.`, { cause: init.cause });\n this.runner = init.runner;\n this.script = init.script;\n this.stderr = init.stderr;\n this.durationMs = init.durationMs;\n if (init.line !== undefined) this.line = init.line;\n }\n}\n\n/** The script printed more than `maxBufferBytes` and was killed. */\nexport class OutputTooLargeError extends OutlookError {\n readonly runner: ScriptRunner;\n readonly maxBufferBytes: number;\n readonly script: string;\n\n constructor(init: { runner: ScriptRunner; maxBufferBytes: number; script: string }) {\n super(\n 'OUTPUT_TOO_LARGE',\n `${init.runner} output exceeded maxBufferBytes (${init.maxBufferBytes}). `\n + 'Raise maxBufferBytes, or narrow the request.',\n );\n this.runner = init.runner;\n this.maxBufferBytes = init.maxBufferBytes;\n this.script = init.script;\n }\n}\n\n/** The run exceeded its time budget and was killed. */\nexport class TimeoutError extends OutlookError {\n readonly runner: ScriptRunner;\n readonly timeoutMs: number;\n readonly script: string;\n\n constructor(init: { runner: ScriptRunner; timeoutMs: number; script: string }) {\n super('TIMEOUT', `${init.runner} run exceeded its ${init.timeoutMs}ms budget and was killed.`);\n this.runner = init.runner;\n this.timeoutMs = init.timeoutMs;\n this.script = init.script;\n }\n}\n\n/** The caller's AbortSignal fired. */\nexport class AbortedError extends OutlookError {\n readonly runner: ScriptRunner;\n\n constructor(runner: ScriptRunner, reason?: unknown) {\n super('ABORTED', `${runner} run was aborted by the caller.`, { cause: reason });\n this.runner = runner;\n }\n}\n\n// ── Typed failures from inside a script ─────────────────────────────────\n//\n// A generated script can only fail by printing a message, so a script that\n// knows WHY it failed — the folder isn't there, the id resolves to nothing —\n// says so by starting the message with a tag: `[outlook-bridge:NOT_FOUND:folder]`.\n// The runner turns a tagged message into the matching error class and strips\n// the tag, so a caller can branch on NOT_FOUND without reading the sentence.\n\n/** The codes a script may raise by tagging its failure message. */\ntype ScriptFailureCode = 'ACCOUNT_NOT_FOUND' | 'NOT_FOUND' | 'INVALID_REQUEST' | 'PERMISSION_DENIED';\n\n/**\n * The tag that opens a typed script failure, ready to be followed by the\n * human-readable message. Plain ASCII with no quote or `$`, so it can sit\n * inside a PowerShell or AppleScript string literal unescaped.\n */\nexport function failureTag(code: 'ACCOUNT_NOT_FOUND' | 'INVALID_REQUEST' | 'PERMISSION_DENIED'): string;\nexport function failureTag(code: 'NOT_FOUND', kind: NotFoundKind): string;\nexport function failureTag(code: ScriptFailureCode, kind?: NotFoundKind): string {\n return `[outlook-bridge:${code}${kind ? `:${kind}` : ''}] `;\n}\n\nconst FAILURE_TAG = /\\[outlook-bridge:(ACCOUNT_NOT_FOUND|NOT_FOUND|INVALID_REQUEST|PERMISSION_DENIED)(?::([a-z]+))?\\]\\s*/;\n\n/** A script's account-not-found message names the address in single quotes. */\nconst QUOTED_ACCOUNT = /'(.*)'/;\n\n/**\n * The typed error a tagged script message stands for, or null when the\n * message carries no tag.\n */\nexport function errorFromTaggedMessage(message: string): OutlookError | null {\n const match = FAILURE_TAG.exec(message);\n if (!match) return null;\n const text = (message.slice(0, match.index) + message.slice(match.index + match[0].length)).trim();\n switch (match[1]) {\n case 'ACCOUNT_NOT_FOUND':\n return new AccountNotFoundError(QUOTED_ACCOUNT.exec(text)?.[1] ?? '', text);\n case 'PERMISSION_DENIED':\n return new PermissionDeniedError(text);\n case 'NOT_FOUND': {\n const kind = NOT_FOUND_KINDS.find(k => k === match[2]) ?? 'email';\n return new NotFoundError(kind, text);\n }\n default:\n return new InvalidRequestError(text);\n }\n}\n\n/**\n * Turn a failed run into the most specific error that can be proven.\n *\n * Order matters. An abort and a timeout both kill the child, so the signal's\n * own state is checked before the kill; an overflow kills it too, and is told\n * apart by its error code. Only then is the script's message worth reading — a\n * process killed mid-write may have printed a partial, misleading one.\n */\nexport function classifyRunFailure(init: {\n runner: ScriptRunner;\n script: string;\n /** Everything the run wrote to stderr. */\n stderr: string;\n /** The script's own failure message, when the runner could isolate one. */\n message?: string;\n /** The script line the failure was raised on, when known. */\n line?: number;\n durationMs: number;\n /** The error `execFile` handed back. */\n nodeError?: (Error & { killed?: boolean; code?: string | number | null }) | null;\n /** The time budget in force, for the TIMEOUT message. */\n timeoutMs?: number;\n /** The stdout cap in force, for the OUTPUT_TOO_LARGE message. */\n maxBufferBytes?: number;\n signal?: AbortSignal;\n}): OutlookError {\n const { runner, script, stderr, durationMs, nodeError, timeoutMs, signal } = init;\n\n if (signal?.aborted || nodeError?.name === 'AbortError' || nodeError?.code === 'ABORT_ERR') {\n return new AbortedError(runner, signal?.reason);\n }\n if (nodeError?.code === 'ERR_CHILD_PROCESS_STDIO_MAXBUFFER') {\n return new OutputTooLargeError({ runner, maxBufferBytes: init.maxBufferBytes ?? 0, script });\n }\n if (nodeError?.killed && timeoutMs) {\n return new TimeoutError({ runner, timeoutMs, script });\n }\n const message = (init.message ?? '').trim() || stderr.trim() || nodeError?.message || '';\n const typed = errorFromTaggedMessage(message);\n if (typed) return typed;\n return new ScriptError({\n runner,\n script,\n stderr,\n durationMs,\n message,\n line: init.line,\n cause: nodeError ?? undefined,\n });\n}\n"]}
|
package/dist/index.d.ts
CHANGED
|
@@ -3,7 +3,7 @@ export { createOutlookBridge, capabilities, supports } from './bridge';
|
|
|
3
3
|
export type { OutlookBridgeInstance } from './bridge';
|
|
4
4
|
export { configure } from './runtime';
|
|
5
5
|
export type { BridgeOptions, BridgeDebugEvent, ResolvedConfig } from './runtime';
|
|
6
|
-
export { OutlookError, UnsupportedPlatformError, NotImplementedError, AccountNotFoundError, NotFoundError, InvalidRequestError, ScriptError, OutputTooLargeError, TimeoutError, AbortedError, } from './errors';
|
|
6
|
+
export { OutlookError, UnsupportedPlatformError, NotImplementedError, AccountNotFoundError, NotFoundError, InvalidRequestError, PermissionDeniedError, ScriptError, OutputTooLargeError, TimeoutError, AbortedError, } from './errors';
|
|
7
7
|
export type { OutlookErrorCode, NotFoundKind, ScriptRunner } from './errors';
|
|
8
8
|
export { hasReplyPrefix, mailFolderRef, splitQuotedOriginal, threadSubject, WELL_KNOWN_FOLDERS } from './mail';
|
|
9
9
|
export { composeTemplateBody, findTemplateMarkers, findTokens, findUnfilledTokens, removeTokenLine, replaceToken, } from './templateBody';
|
package/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
4
|
-
exports.replaceToken = exports.removeTokenLine = exports.findUnfilledTokens = void 0;
|
|
3
|
+
exports.findTemplateMarkers = exports.composeTemplateBody = exports.WELL_KNOWN_FOLDERS = exports.threadSubject = exports.splitQuotedOriginal = exports.mailFolderRef = exports.hasReplyPrefix = exports.AbortedError = exports.TimeoutError = exports.OutputTooLargeError = exports.ScriptError = exports.PermissionDeniedError = exports.InvalidRequestError = exports.NotFoundError = exports.AccountNotFoundError = exports.NotImplementedError = exports.UnsupportedPlatformError = exports.OutlookError = exports.configure = exports.supports = exports.capabilities = exports.createOutlookBridge = exports.editEmailTemplate = exports.saveTemplateEmail = exports.readTemplateEmails = exports.readOutlookSignatureHtml = exports.listOutlookSignatures = exports.readBounceReport = exports.readSentRecipientGroups = exports.collectBouncedRecipients = exports.cleanUndeliverableEmails = exports.saveEmailAttachments = exports.saveEmailAttachment = exports.purgeDeletedItems = exports.deleteOutlookEmails = exports.deleteOutlookDrafts = exports.sendAllDrafts = exports.sendDrafts = exports.listOutlookDrafts = exports.moveOutlookEmails = exports.listInboxFolders = exports.locateEmail = exports.openOutlookEmail = exports.readEmailBody = exports.readSelectedEmail = exports.searchInboxByFilter = exports.readInboxEmails = exports.replyOutlookEmail = exports.sendOutlookEmail = exports.getOutlookAccounts = void 0;
|
|
4
|
+
exports.replaceToken = exports.removeTokenLine = exports.findUnfilledTokens = exports.findTokens = void 0;
|
|
5
5
|
// The public API. Listed explicitly rather than `export *`, so the package
|
|
6
6
|
// exposes only what it means to support; the `exports` map in package.json
|
|
7
7
|
// closes the deep-import path around it.
|
|
@@ -24,6 +24,7 @@ Object.defineProperty(exports, "NotImplementedError", { enumerable: true, get: f
|
|
|
24
24
|
Object.defineProperty(exports, "AccountNotFoundError", { enumerable: true, get: function () { return errors_1.AccountNotFoundError; } });
|
|
25
25
|
Object.defineProperty(exports, "NotFoundError", { enumerable: true, get: function () { return errors_1.NotFoundError; } });
|
|
26
26
|
Object.defineProperty(exports, "InvalidRequestError", { enumerable: true, get: function () { return errors_1.InvalidRequestError; } });
|
|
27
|
+
Object.defineProperty(exports, "PermissionDeniedError", { enumerable: true, get: function () { return errors_1.PermissionDeniedError; } });
|
|
27
28
|
Object.defineProperty(exports, "ScriptError", { enumerable: true, get: function () { return errors_1.ScriptError; } });
|
|
28
29
|
Object.defineProperty(exports, "OutputTooLargeError", { enumerable: true, get: function () { return errors_1.OutputTooLargeError; } });
|
|
29
30
|
Object.defineProperty(exports, "TimeoutError", { enumerable: true, get: function () { return errors_1.TimeoutError; } });
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;AAAA,2EAA2E;AAC3E,2EAA2E;AAC3E,yCAAyC;AACzC,qCAAyC;AAEzC,gFAAgF;AAE5E,0BAAkB,GA4BlB,sBAAa,qBA3Bb,wBAAgB,GA2BhB,sBAAa,mBA1Bb,yBAAiB,GA0BjB,sBAAa,oBAzBb,uBAAe,GAyBf,sBAAa,kBAxBb,2BAAmB,GAwBnB,sBAAa,sBAvBb,yBAAiB,GAuBjB,sBAAa,oBAtBb,qBAAa,GAsBb,sBAAa,gBArBb,wBAAgB,GAqBhB,sBAAa,mBApBb,mBAAW,GAoBX,sBAAa,cAnBb,wBAAgB,GAmBhB,sBAAa,mBAlBb,yBAAiB,GAkBjB,sBAAa,oBAjBb,yBAAiB,GAiBjB,sBAAa,oBAhBb,kBAAU,GAgBV,sBAAa,aAfb,qBAAa,GAeb,sBAAa,gBAdb,2BAAmB,GAcnB,sBAAa,sBAbb,2BAAmB,GAanB,sBAAa,sBAZb,yBAAiB,GAYjB,sBAAa,oBAXb,2BAAmB,GAWnB,sBAAa,sBAVb,4BAAoB,GAUpB,sBAAa,uBATb,gCAAwB,GASxB,sBAAa,2BARb,gCAAwB,GAQxB,sBAAa,2BAPb,+BAAuB,GAOvB,sBAAa,0BANb,wBAAgB,GAMhB,sBAAa,mBALb,6BAAqB,GAKrB,sBAAa,wBAJb,gCAAwB,GAIxB,sBAAa,2BAHb,0BAAkB,GAGlB,sBAAa,qBAFb,yBAAiB,GAEjB,sBAAa,oBADb,yBAAiB,GACjB,sBAAa,mBAAC;AAElB,yEAAyE;AACzE,mCAAuE;AAA9D,6GAAA,mBAAmB,OAAA;AAAE,sGAAA,YAAY,OAAA;AAAE,kGAAA,QAAQ,OAAA;AAGpD,yBAAyB;AACzB,qCAAsC;AAA7B,oGAAA,SAAS,OAAA;AAGlB,2EAA2E;AAC3E,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;AAAA,2EAA2E;AAC3E,2EAA2E;AAC3E,yCAAyC;AACzC,qCAAyC;AAEzC,gFAAgF;AAE5E,0BAAkB,GA4BlB,sBAAa,qBA3Bb,wBAAgB,GA2BhB,sBAAa,mBA1Bb,yBAAiB,GA0BjB,sBAAa,oBAzBb,uBAAe,GAyBf,sBAAa,kBAxBb,2BAAmB,GAwBnB,sBAAa,sBAvBb,yBAAiB,GAuBjB,sBAAa,oBAtBb,qBAAa,GAsBb,sBAAa,gBArBb,wBAAgB,GAqBhB,sBAAa,mBApBb,mBAAW,GAoBX,sBAAa,cAnBb,wBAAgB,GAmBhB,sBAAa,mBAlBb,yBAAiB,GAkBjB,sBAAa,oBAjBb,yBAAiB,GAiBjB,sBAAa,oBAhBb,kBAAU,GAgBV,sBAAa,aAfb,qBAAa,GAeb,sBAAa,gBAdb,2BAAmB,GAcnB,sBAAa,sBAbb,2BAAmB,GAanB,sBAAa,sBAZb,yBAAiB,GAYjB,sBAAa,oBAXb,2BAAmB,GAWnB,sBAAa,sBAVb,4BAAoB,GAUpB,sBAAa,uBATb,gCAAwB,GASxB,sBAAa,2BARb,gCAAwB,GAQxB,sBAAa,2BAPb,+BAAuB,GAOvB,sBAAa,0BANb,wBAAgB,GAMhB,sBAAa,mBALb,6BAAqB,GAKrB,sBAAa,wBAJb,gCAAwB,GAIxB,sBAAa,2BAHb,0BAAkB,GAGlB,sBAAa,qBAFb,yBAAiB,GAEjB,sBAAa,oBADb,yBAAiB,GACjB,sBAAa,mBAAC;AAElB,yEAAyE;AACzE,mCAAuE;AAA9D,6GAAA,mBAAmB,OAAA;AAAE,sGAAA,YAAY,OAAA;AAAE,kGAAA,QAAQ,OAAA;AAGpD,yBAAyB;AACzB,qCAAsC;AAA7B,oGAAA,SAAS,OAAA;AAGlB,2EAA2E;AAC3E,mCAYkB;AAXd,sGAAA,YAAY,OAAA;AACZ,kHAAA,wBAAwB,OAAA;AACxB,6GAAA,mBAAmB,OAAA;AACnB,8GAAA,oBAAoB,OAAA;AACpB,uGAAA,aAAa,OAAA;AACb,6GAAA,mBAAmB,OAAA;AACnB,+GAAA,qBAAqB,OAAA;AACrB,qGAAA,WAAW,OAAA;AACX,6GAAA,mBAAmB,OAAA;AACnB,sGAAA,YAAY,OAAA;AACZ,sGAAA,YAAY,OAAA;AAIhB,oDAAoD;AACpD,+BAA+G;AAAtG,sGAAA,cAAc,OAAA;AAAE,qGAAA,aAAa,OAAA;AAAE,2GAAA,mBAAmB,OAAA;AAAE,qGAAA,aAAa,OAAA;AAAE,0GAAA,kBAAkB,OAAA;AAC9F,+CAOwB;AANpB,mHAAA,mBAAmB,OAAA;AACnB,mHAAA,mBAAmB,OAAA;AACnB,0GAAA,UAAU,OAAA;AACV,kHAAA,kBAAkB,OAAA;AAClB,+GAAA,eAAe,OAAA;AACf,4GAAA,YAAY,OAAA","sourcesContent":["// The public API. Listed explicitly rather than `export *`, so the package\n// exposes only what it means to support; the `exports` map in package.json\n// closes the deep-import path around it.\nimport { defaultBridge } from './bridge';\n\n// Every operation as a plain function, running under the process-wide settings.\nexport const {\n getOutlookAccounts,\n sendOutlookEmail,\n replyOutlookEmail,\n readInboxEmails,\n searchInboxByFilter,\n readSelectedEmail,\n readEmailBody,\n openOutlookEmail,\n locateEmail,\n listInboxFolders,\n moveOutlookEmails,\n listOutlookDrafts,\n sendDrafts,\n sendAllDrafts,\n deleteOutlookDrafts,\n deleteOutlookEmails,\n purgeDeletedItems,\n saveEmailAttachment,\n saveEmailAttachments,\n cleanUndeliverableEmails,\n collectBouncedRecipients,\n readSentRecipientGroups,\n readBounceReport,\n listOutlookSignatures,\n readOutlookSignatureHtml,\n readTemplateEmails,\n saveTemplateEmail,\n editEmailTemplate,\n} = defaultBridge;\n\n// A bridge with its own settings, and asking what works on this machine.\nexport { createOutlookBridge, capabilities, supports } from './bridge';\nexport type { OutlookBridgeInstance } from './bridge';\n\n// Process-wide settings.\nexport { configure } from './runtime';\nexport type { BridgeOptions, BridgeDebugEvent, ResolvedConfig } from './runtime';\n\n// Failures carry a stable `code`; the messages beside them are not an API.\nexport {\n OutlookError,\n UnsupportedPlatformError,\n NotImplementedError,\n AccountNotFoundError,\n NotFoundError,\n InvalidRequestError,\n PermissionDeniedError,\n ScriptError,\n OutputTooLargeError,\n TimeoutError,\n AbortedError,\n} from './errors';\nexport type { OutlookErrorCode, NotFoundKind, ScriptRunner } from './errors';\n\n// Platform-neutral helpers, usable without Outlook.\nexport { hasReplyPrefix, mailFolderRef, splitQuotedOriginal, threadSubject, WELL_KNOWN_FOLDERS } from './mail';\nexport {\n composeTemplateBody,\n findTemplateMarkers,\n findTokens,\n findUnfilledTokens,\n removeTokenLine,\n replaceToken,\n} from './templateBody';\nexport type { ComposeOptions, TemplateMarkers } from './templateBody';\n\nexport type {\n OutlookBridge,\n BridgeCapability,\n CapabilityMap,\n EmailLocator,\n EmailRef,\n Recipients,\n SendEmailParams,\n SendEmailResult,\n ReplyEmailParams,\n ReplyEmailResult,\n ItemFailure,\n ReadInboxOptions,\n InboxEmail,\n InboxSearchFilter,\n InboxSearchMatch,\n EmailDescription,\n LocateEmailOptions,\n SelectedEmail,\n ReadEmailBodyOptions,\n EmailBodyResult,\n MailFolderRef,\n ListFoldersOptions,\n InboxFolderInfo,\n MoveEmailsOptions,\n MoveEmailsResult,\n ListDraftsOptions,\n ListDraftsResult,\n OutlookDraft,\n SendDraftsResult,\n DeleteDraftsResult,\n DeleteMailOptions,\n DeleteMailOutcome,\n DeleteMailResult,\n PurgeDeletedItemsOptions,\n PurgeDeletedItemsResult,\n SaveAttachmentOptions,\n SavedAttachment,\n CleanUndeliverableOptions,\n CleanUndeliverableResult,\n UndeliverableEmail,\n CollectBouncedRecipientsOptions,\n SentRecipientGroupsOptions,\n SentRecipientGroup,\n BounceReportOptions,\n BounceReport,\n BouncedSend,\n ReadTemplatesOptions,\n TemplateEmail,\n TemplateFolderResult,\n SaveTemplateParams,\n SaveTemplateResult,\n} from './types';\n"]}
|
package/dist/mac/accounts.d.ts
CHANGED
|
@@ -6,5 +6,9 @@
|
|
|
6
6
|
* 365 mailbox added by the modern sync engine belongs to none of them. So the
|
|
7
7
|
* profile database is asked as well and the answers merged — the script's
|
|
8
8
|
* first, since those are the accounts every operation supports fully.
|
|
9
|
+
*
|
|
10
|
+
* When neither finds anything and macOS refused the profile, that refusal is the
|
|
11
|
+
* answer, not an empty list: Outlook may well hold mailboxes nothing here could
|
|
12
|
+
* see. A list that found some is returned as it is, since what it holds works.
|
|
9
13
|
*/
|
|
10
14
|
export declare function getOutlookAccounts(): Promise<string[]>;
|
package/dist/mac/accounts.js
CHANGED
|
@@ -5,6 +5,7 @@ exports.getOutlookAccounts = getOutlookAccounts;
|
|
|
5
5
|
const run_1 = require("./run");
|
|
6
6
|
const profile_1 = require("./profile");
|
|
7
7
|
const scripts_1 = require("./scripts");
|
|
8
|
+
const errors_1 = require("../errors");
|
|
8
9
|
/**
|
|
9
10
|
* Every mailbox's SMTP address. Empty under New Outlook, which doesn't expose
|
|
10
11
|
* accounts to AppleScript.
|
|
@@ -13,6 +14,10 @@ const scripts_1 = require("./scripts");
|
|
|
13
14
|
* 365 mailbox added by the modern sync engine belongs to none of them. So the
|
|
14
15
|
* profile database is asked as well and the answers merged — the script's
|
|
15
16
|
* first, since those are the accounts every operation supports fully.
|
|
17
|
+
*
|
|
18
|
+
* When neither finds anything and macOS refused the profile, that refusal is the
|
|
19
|
+
* answer, not an empty list: Outlook may well hold mailboxes nothing here could
|
|
20
|
+
* see. A list that found some is returned as it is, since what it holds works.
|
|
16
21
|
*/
|
|
17
22
|
async function getOutlookAccounts() {
|
|
18
23
|
const [raw, profile] = await Promise.all([
|
|
@@ -29,6 +34,11 @@ return acctList as string`, 'quick'),
|
|
|
29
34
|
seen.add(emailAccount.toLowerCase());
|
|
30
35
|
accounts.push(emailAccount);
|
|
31
36
|
}
|
|
37
|
+
if (accounts.length === 0) {
|
|
38
|
+
const refusal = await (0, profile_1.profileRefusal)();
|
|
39
|
+
if (refusal)
|
|
40
|
+
throw new errors_1.PermissionDeniedError(`No Outlook mailbox could be listed. ${refusal}`);
|
|
41
|
+
}
|
|
32
42
|
return accounts;
|
|
33
43
|
}
|
|
34
44
|
//# sourceMappingURL=accounts.js.map
|
package/dist/mac/accounts.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"accounts.js","sourceRoot":"","sources":["../../src/mac/accounts.ts"],"names":[],"mappings":";;
|
|
1
|
+
{"version":3,"file":"accounts.js","sourceRoot":"","sources":["../../src/mac/accounts.ts"],"names":[],"mappings":";;AAmBA,gDAmBC;AAtCD,2CAA2C;AAC3C,+BAAqC;AACrC,uCAAgE;AAChE,uCAAkD;AAClD,sCAAkD;AAElD;;;;;;;;;;;;GAYG;AACI,KAAK,UAAU,kBAAkB;IACpC,MAAM,CAAC,GAAG,EAAE,OAAO,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC;QACrC,IAAA,kBAAY,EAAC,GAAG,+BAAqB;;0BAEnB,EAAE,OAAO,CAAC;QAC5B,IAAA,6BAAmB,GAAE;KACxB,CAAC,CAAC;IACH,MAAM,QAAQ,GAAG,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IACpE,MAAM,IAAI,GAAG,IAAI,GAAG,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC;IACzD,KAAK,MAAM,EAAE,YAAY,EAAE,IAAI,OAAO,EAAE,CAAC;QACrC,IAAI,IAAI,CAAC,GAAG,CAAC,YAAY,CAAC,WAAW,EAAE,CAAC;YAAE,SAAS;QACnD,IAAI,CAAC,GAAG,CAAC,YAAY,CAAC,WAAW,EAAE,CAAC,CAAC;QACrC,QAAQ,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;IAChC,CAAC;IACD,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACxB,MAAM,OAAO,GAAG,MAAM,IAAA,wBAAc,GAAE,CAAC;QACvC,IAAI,OAAO;YAAE,MAAM,IAAI,8BAAqB,CAAC,uCAAuC,OAAO,EAAE,CAAC,CAAC;IACnG,CAAC;IACD,OAAO,QAAQ,CAAC;AACpB,CAAC","sourcesContent":["// The mailboxes Outlook for Mac can reach.\nimport { runOsaScript } from './run';\nimport { profileRefusal, readProfileAccounts } from './profile';\nimport { LIST_ACCOUNTS_SNIPPET } from './scripts';\nimport { PermissionDeniedError } from '../errors';\n\n/**\n * Every mailbox's SMTP address. Empty under New Outlook, which doesn't expose\n * accounts to AppleScript.\n *\n * The dictionary's three account classes aren't the whole profile: a Microsoft\n * 365 mailbox added by the modern sync engine belongs to none of them. So the\n * profile database is asked as well and the answers merged — the script's\n * first, since those are the accounts every operation supports fully.\n *\n * When neither finds anything and macOS refused the profile, that refusal is the\n * answer, not an empty list: Outlook may well hold mailboxes nothing here could\n * see. A list that found some is returned as it is, since what it holds works.\n */\nexport async function getOutlookAccounts(): Promise<string[]> {\n const [raw, profile] = await Promise.all([\n runOsaScript(`${LIST_ACCOUNTS_SNIPPET}\nset AppleScript's text item delimiters to linefeed\nreturn acctList as string`, 'quick'),\n readProfileAccounts(),\n ]);\n const accounts = raw.split('\\n').map(s => s.trim()).filter(Boolean);\n const seen = new Set(accounts.map(a => a.toLowerCase()));\n for (const { emailAccount } of profile) {\n if (seen.has(emailAccount.toLowerCase())) continue;\n seen.add(emailAccount.toLowerCase());\n accounts.push(emailAccount);\n }\n if (accounts.length === 0) {\n const refusal = await profileRefusal();\n if (refusal) throw new PermissionDeniedError(`No Outlook mailbox could be listed. ${refusal}`);\n }\n return accounts;\n}\n"]}
|
package/dist/mac/bounces.js
CHANGED
|
@@ -51,15 +51,17 @@ ${(0, scripts_1.rootFolderSnippet)(acct, folderTerm, 'scanFolder')}
|
|
|
51
51
|
set hdrList to {}
|
|
52
52
|
end try
|
|
53
53
|
set hasHeaders to ((count of hdrList) is (count of idList))
|
|
54
|
+
set hasTimes to ((count of timeList) is (count of idList))
|
|
55
|
+
set hasSenders to ((count of sndList) is (count of idList))
|
|
54
56
|
set cutoff to (current date) - (${(0, run_1.asInt)(daysBack)} * days)
|
|
55
57
|
set out to ""
|
|
56
58
|
repeat with i from 1 to (count of idList)
|
|
57
59
|
set stamp to missing value
|
|
58
|
-
if
|
|
60
|
+
if hasTimes then set stamp to item i of timeList
|
|
59
61
|
if stamp is missing value or stamp is greater than or equal to cutoff then
|
|
60
62
|
set sndName to ""
|
|
61
63
|
set sndAddr to ""
|
|
62
|
-
if
|
|
64
|
+
if hasSenders then
|
|
63
65
|
set snd to item i of sndList
|
|
64
66
|
try
|
|
65
67
|
set sndAddr to (address of snd) as string
|
|
@@ -100,15 +102,9 @@ async function readBodies(ids) {
|
|
|
100
102
|
const raw = await (0, run_1.runOsaScript)(`tell application "Microsoft Outlook"
|
|
101
103
|
set out to ""
|
|
102
104
|
repeat with theId in ${(0, run_1.asIdList)(ids)}
|
|
103
|
-
|
|
104
|
-
try
|
|
105
|
-
set theMsg to message id theId
|
|
106
|
-
end try
|
|
105
|
+
${(0, scripts_1.messageByIdSnippet)(' ')}
|
|
107
106
|
if theMsg is not missing value then
|
|
108
|
-
|
|
109
|
-
try
|
|
110
|
-
set bodyText to (plain text content of theMsg) as string
|
|
111
|
-
end try
|
|
107
|
+
${(0, scripts_1.bodyTextSnippet)(' ')}
|
|
112
108
|
set out to out & ${(0, run_1.asRow)(['(theId as string)', 'bodyText'])}
|
|
113
109
|
end if
|
|
114
110
|
end repeat
|
|
@@ -155,10 +151,7 @@ async function cleanUndeliverableEmails(request) {
|
|
|
155
151
|
repeat with theId in ${(0, run_1.asIdList)(matched.map(m => m.entryId))}
|
|
156
152
|
set subj to ""
|
|
157
153
|
try
|
|
158
|
-
|
|
159
|
-
try
|
|
160
|
-
set theMsg to message id theId
|
|
161
|
-
end try
|
|
154
|
+
${(0, scripts_1.messageByIdSnippet)(' ')}
|
|
162
155
|
if theMsg is missing value then error "message is no longer in the mailbox"
|
|
163
156
|
try
|
|
164
157
|
set subj to (subject of theMsg) as string
|
|
@@ -214,11 +207,12 @@ ${(0, scripts_1.rootFolderSnippet)(acct, 'sent items', 'sentFolder')}
|
|
|
214
207
|
set timeList to {}
|
|
215
208
|
end try
|
|
216
209
|
set subjList to subject of every message of sentFolder
|
|
210
|
+
set hasTimes to ((count of timeList) is (count of idList))
|
|
217
211
|
set cutoff to (current date) - (${(0, run_1.asInt)(request.daysBack)} * days)
|
|
218
212
|
set out to ""
|
|
219
213
|
repeat with i from 1 to (count of idList)
|
|
220
214
|
set stamp to missing value
|
|
221
|
-
if
|
|
215
|
+
if hasTimes then set stamp to item i of timeList
|
|
222
216
|
if stamp is missing value or stamp is greater than or equal to cutoff then
|
|
223
217
|
set out to out & ${(0, run_1.asRow)(['(item i of idList as string)', '(item i of subjList)', 'my isoDate(stamp)'])}
|
|
224
218
|
end if
|
|
@@ -237,10 +231,7 @@ end tell`, 'scan')).map(record => {
|
|
|
237
231
|
const raw = await (0, run_1.runOsaScript)(`tell application "Microsoft Outlook"
|
|
238
232
|
set out to ""
|
|
239
233
|
repeat with theId in ${(0, run_1.asIdList)(chosen.map(row => row.id))}
|
|
240
|
-
|
|
241
|
-
try
|
|
242
|
-
set theMsg to message id theId
|
|
243
|
-
end try
|
|
234
|
+
${(0, scripts_1.messageByIdSnippet)(' ')}
|
|
244
235
|
if theMsg is not missing value then
|
|
245
236
|
${(0, scripts_1.allRecipientsSnippet)(' ')}
|
|
246
237
|
set out to out & ${(0, run_1.asRow)(['(theId as string)', 'my sanitizeList(addrList)'])}
|
package/dist/mac/bounces.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bounces.js","sourceRoot":"","sources":["../../src/mac/bounces.ts"],"names":[],"mappings":"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","sourcesContent":["// Bounce-backs: finding them, clearing them away, and mining them for the\n// addresses that failed.\n//\n// Windows classifies inside PowerShell, after Restrict has narrowed the set.\n// AppleScript has no Restrict, so a folder is indexed with bulk property reads\n// and the SAME rules (shared/bounceRules) run here in TypeScript. Only matches\n// have their bodies read, which keeps either version affordable.\nimport {\n asIdList,\n asInt,\n asRow,\n asString,\n boolField,\n field,\n intField,\n ITEM_FAILURE_ROW,\n runOsaScript,\n splitBatch,\n splitFields,\n splitList,\n splitRecords,\n} from './run';\nimport { accountLookupSnippet, allRecipientsSnippet, resolveMacAccount, rootFolderSnippet } from './scripts';\nimport { bounceReason, DSN_HEADER_MARKERS, failedRecipients } from '../shared/bounceRules';\nimport type { CleanUndeliverableRequest, CollectBouncesRequest, SentGroupsRequest } from '../backend';\nimport type { CleanUndeliverableResult, ItemFailure, SentRecipientGroup, UndeliverableEmail } from '../types';\n\ninterface IndexedMessage {\n id: string;\n subject: string;\n senderName: string;\n senderEmail: string;\n receivedTime: string;\n /** Whether the message's own headers declare it a delivery-status notification. */\n isNonDeliveryReport: boolean;\n}\n\n/**\n * Index one well-known folder's messages within the window: everything the\n * classifier needs, in bulk reads — `sender of every message` hands back a list\n * of records AppleScript reads locally, so the whole index is five Apple events.\n *\n * The fifth is the header block, which carries the structural bounce signal (see\n * DSN_HEADER_MARKERS). Headers are bulkier than the other properties, so they\n * are tested in AppleScript and cross the boundary as one flag per message\n * rather than as text.\n */\nasync function indexFolder(emailAccount: string, folderTerm: string, daysBack: number): Promise<IndexedMessage[]> {\n const acct = await resolveMacAccount(emailAccount);\n const raw = await runOsaScript(`tell application \"Microsoft Outlook\"\n${accountLookupSnippet(acct)}\n${rootFolderSnippet(acct, folderTerm, 'scanFolder')}\n set idList to id of every message of scanFolder\n set subjList to subject of every message of scanFolder\n try\n set timeList to time received of every message of scanFolder\n on error\n set timeList to {}\n end try\n try\n set sndList to sender of every message of scanFolder\n on error\n set sndList to {}\n end try\n -- Outlook for Mac publishes no MessageClass, so the delivery-status headers\n -- the report arrived with are the structural signal instead. A folder whose\n -- headers can't be read leaves the list empty and every flag false, which\n -- classifies on the sender and subject rules alone — the behaviour before\n -- this read existed, never an error.\n try\n set hdrList to headers of every message of scanFolder\n on error\n set hdrList to {}\n end try\n set hasHeaders to ((count of hdrList) is (count of idList))\n set cutoff to (current date) - (${asInt(daysBack)} * days)\n set out to \"\"\n repeat with i from 1 to (count of idList)\n set stamp to missing value\n if (count of timeList) is (count of idList) then set stamp to item i of timeList\n if stamp is missing value or stamp is greater than or equal to cutoff then\n set sndName to \"\"\n set sndAddr to \"\"\n if (count of sndList) is (count of idList) then\n set snd to item i of sndList\n try\n set sndAddr to (address of snd) as string\n end try\n try\n set sndName to (name of snd) as string\n end try\n end if\n set isNdr to false\n if hasHeaders then\n try\n set hdrText to (item i of hdrList) as string\n if ${DSN_HEADER_MARKERS.map(marker => `hdrText contains ${asString(marker)}`).join(' and ')} then set isNdr to true\n end try\n end if\n set out to out & ${asRow(['(item i of idList as string)', '(item i of subjList)', 'sndName', 'sndAddr', 'my isoDate(stamp)', '(isNdr as string)'])}\n end if\n end repeat\n return out\nend tell`, 'scan');\n return splitRecords(raw).map(record => {\n const parts = splitFields(record);\n return {\n id: field(parts, 0),\n subject: field(parts, 1).trim(),\n senderName: field(parts, 2),\n senderEmail: field(parts, 3),\n receivedTime: field(parts, 4),\n isNonDeliveryReport: boolField(parts, 5),\n };\n });\n}\n\n/** The plain-text bodies of the given messages, by id. */\nasync function readBodies(ids: readonly string[]): Promise<Map<string, string>> {\n const bodies = new Map<string, string>();\n if (ids.length === 0) return bodies;\n const raw = await runOsaScript(`tell application \"Microsoft Outlook\"\n set out to \"\"\n repeat with theId in ${asIdList(ids)}\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 (plain text content 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`, 'scan');\n for (const record of splitRecords(raw)) {\n const parts = splitFields(record);\n bodies.set(field(parts, 0), field(parts, 1));\n }\n return bodies;\n}\n\n/** Classify an index, then fill each match's failed recipients from its body. */\nasync function collectBounces(indexed: readonly IndexedMessage[], accountAddress: string): Promise<UndeliverableEmail[]> {\n const matched = indexed\n .map(message => ({ message, reason: bounceReason(message) }))\n .filter(entry => entry.reason !== '');\n if (matched.length === 0) return [];\n const bodies = await readBodies(matched.map(entry => entry.message.id));\n return matched.map(({ message, reason }) => ({\n entryId: message.id,\n subject: message.subject,\n senderName: message.senderName,\n senderEmail: message.senderEmail,\n receivedTime: message.receivedTime,\n matchedReason: reason,\n failedRecipients: failedRecipients(bodies.get(message.id) || '', accountAddress),\n }));\n}\n\n/**\n * Find bounce-backs in the Inbox and, unless dry-running, move each to Deleted\n * Items. The same three signals Windows uses apply, in the same order: a report\n * that declares itself one in its headers, then the sender fingerprint, then the\n * subject phrases.\n */\nexport async function cleanUndeliverableEmails(request: CleanUndeliverableRequest): Promise<CleanUndeliverableResult> {\n const matched = await collectBounces(await indexFolder(request.account, 'inbox', request.daysBack), request.account);\n let deletedCount = 0;\n let failed: ItemFailure[] = [];\n if (!request.dryRun && matched.length > 0) {\n const batch = splitBatch(await runOsaScript(`tell application \"Microsoft Outlook\"\n set deletedCount to 0\n set out to \"\"\n repeat with theId in ${asIdList(matched.map(m => m.entryId))}\n set subj to \"\"\n try\n set theMsg to missing value\n try\n set theMsg to message id theId\n end try\n if theMsg is missing value then error \"message is no longer in the mailbox\"\n try\n set subj to (subject of theMsg) as string\n end try\n delete theMsg\n set deletedCount to deletedCount + 1\n on error errText\n set out to out & ${ITEM_FAILURE_ROW}\n end try\n end repeat\n return ${asRow(['(deletedCount as string)'])} & out\nend tell`, 'scan'));\n deletedCount = intField(batch.summary, 0);\n failed = batch.failed;\n }\n return {\n account: request.account,\n scannedDays: request.daysBack,\n dryRun: request.dryRun,\n matchedCount: matched.length,\n deletedCount,\n matched,\n failed,\n };\n}\n\n/** The deduplicated addresses bounce-backs report as failed. Never deletes anything. */\nexport async function collectBouncedRecipients(request: CollectBouncesRequest): Promise<string[]> {\n const terms = request.includeDeletedItems ? ['inbox', 'deleted items'] : ['inbox'];\n const found = new Set<string>();\n for (const term of terms) {\n const bounces = await collectBounces(await indexFolder(request.account, term, request.daysBack), request.account);\n for (const bounce of bounces) {\n for (const address of bounce.failedRecipients) found.add(address);\n }\n }\n return [...found];\n}\n\n/**\n * Sent Items within the window, each message with every address it went to,\n * newest first. Indexed on `time sent` — outgoing mail's own stamp; indexing on\n * `time received` yields an empty set that looks exactly like an empty folder.\n */\nexport async function readSentRecipientGroups(request: SentGroupsRequest): Promise<SentRecipientGroup[]> {\n const acct = await resolveMacAccount(request.account);\n const indexed = splitRecords(await runOsaScript(`tell application \"Microsoft Outlook\"\n${accountLookupSnippet(acct)}\n${rootFolderSnippet(acct, 'sent items', 'sentFolder')}\n set idList to id of every message of sentFolder\n try\n set timeList to time sent of every message of sentFolder\n on error\n set timeList to {}\n end try\n set subjList to subject of every message of sentFolder\n set cutoff to (current date) - (${asInt(request.daysBack)} * days)\n set out to \"\"\n repeat with i from 1 to (count of idList)\n set stamp to missing value\n if (count of timeList) is (count of idList) then set stamp to item i of timeList\n if stamp is missing value or stamp is greater than or equal to cutoff then\n set out to out & ${asRow(['(item i of idList as string)', '(item i of subjList)', 'my isoDate(stamp)'])}\n end if\n end repeat\n return out\nend tell`, 'scan')).map(record => {\n const parts = splitFields(record);\n return { id: field(parts, 0), subject: field(parts, 1).trim(), sentOn: field(parts, 2) };\n });\n indexed.sort((a, b) => b.sentOn.localeCompare(a.sentOn));\n const chosen = indexed.slice(0, request.limit);\n if (chosen.length === 0) return [];\n\n // Pass 2 — recipients, which can only be read message by message.\n const recipientsById = 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(row => row.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${allRecipientsSnippet(' ')}\n set out to out & ${asRow(['(theId as string)', 'my sanitizeList(addrList)'])}\n end if\n end repeat\n return out\nend tell`, 'scan');\n for (const record of splitRecords(raw)) {\n const parts = splitFields(record);\n recipientsById.set(field(parts, 0), splitList(field(parts, 1)).map(address => address.toLowerCase()));\n }\n return chosen\n .map(row => ({\n entryId: row.id,\n subject: row.subject,\n sentOn: row.sentOn,\n recipients: recipientsById.get(row.id) ?? [],\n }))\n .filter(group => group.recipients.length > 0);\n}\n"]}
|
|
1
|
+
{"version":3,"file":"bounces.js","sourceRoot":"","sources":["../../src/mac/bounces.ts"],"names":[],"mappings":"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","sourcesContent":["// Bounce-backs: finding them, clearing them away, and mining them for the\n// addresses that failed.\n//\n// Windows classifies inside PowerShell, after Restrict has narrowed the set.\n// AppleScript has no Restrict, so a folder is indexed with bulk property reads\n// and the SAME rules (shared/bounceRules) run here in TypeScript. Only matches\n// have their bodies read, which keeps either version affordable.\nimport {\n asIdList,\n asInt,\n asRow,\n asString,\n boolField,\n field,\n intField,\n ITEM_FAILURE_ROW,\n runOsaScript,\n splitBatch,\n splitFields,\n splitList,\n splitRecords,\n} from './run';\nimport {\n accountLookupSnippet,\n allRecipientsSnippet,\n bodyTextSnippet,\n messageByIdSnippet,\n resolveMacAccount,\n rootFolderSnippet,\n} from './scripts';\nimport { bounceReason, DSN_HEADER_MARKERS, failedRecipients } from '../shared/bounceRules';\nimport type { CleanUndeliverableRequest, CollectBouncesRequest, SentGroupsRequest } from '../backend';\nimport type { CleanUndeliverableResult, ItemFailure, SentRecipientGroup, UndeliverableEmail } from '../types';\n\ninterface IndexedMessage {\n id: string;\n subject: string;\n senderName: string;\n senderEmail: string;\n receivedTime: string;\n /** Whether the message's own headers declare it a delivery-status notification. */\n isNonDeliveryReport: boolean;\n}\n\n/**\n * Index one well-known folder's messages within the window: everything the\n * classifier needs, in bulk reads — `sender of every message` hands back a list\n * of records AppleScript reads locally, so the whole index is five Apple events.\n *\n * The fifth is the header block, which carries the structural bounce signal (see\n * DSN_HEADER_MARKERS). Headers are bulkier than the other properties, so they\n * are tested in AppleScript and cross the boundary as one flag per message\n * rather than as text.\n */\nasync function indexFolder(emailAccount: string, folderTerm: string, daysBack: number): Promise<IndexedMessage[]> {\n const acct = await resolveMacAccount(emailAccount);\n const raw = await runOsaScript(`tell application \"Microsoft Outlook\"\n${accountLookupSnippet(acct)}\n${rootFolderSnippet(acct, folderTerm, 'scanFolder')}\n set idList to id of every message of scanFolder\n set subjList to subject of every message of scanFolder\n try\n set timeList to time received of every message of scanFolder\n on error\n set timeList to {}\n end try\n try\n set sndList to sender of every message of scanFolder\n on error\n set sndList to {}\n end try\n -- Outlook for Mac publishes no MessageClass, so the delivery-status headers\n -- the report arrived with are the structural signal instead. A folder whose\n -- headers can't be read leaves the list empty and every flag false, which\n -- classifies on the sender and subject rules alone — the behaviour before\n -- this read existed, never an error.\n try\n set hdrList to headers of every message of scanFolder\n on error\n set hdrList to {}\n end try\n set hasHeaders to ((count of hdrList) is (count of idList))\n set hasTimes to ((count of timeList) is (count of idList))\n set hasSenders to ((count of sndList) is (count of idList))\n set cutoff to (current date) - (${asInt(daysBack)} * days)\n set out to \"\"\n repeat with i from 1 to (count of idList)\n set stamp to missing value\n if hasTimes then set stamp to item i of timeList\n if stamp is missing value or stamp is greater than or equal to cutoff then\n set sndName to \"\"\n set sndAddr to \"\"\n if hasSenders then\n set snd to item i of sndList\n try\n set sndAddr to (address of snd) as string\n end try\n try\n set sndName to (name of snd) as string\n end try\n end if\n set isNdr to false\n if hasHeaders then\n try\n set hdrText to (item i of hdrList) as string\n if ${DSN_HEADER_MARKERS.map(marker => `hdrText contains ${asString(marker)}`).join(' and ')} then set isNdr to true\n end try\n end if\n set out to out & ${asRow(['(item i of idList as string)', '(item i of subjList)', 'sndName', 'sndAddr', 'my isoDate(stamp)', '(isNdr as string)'])}\n end if\n end repeat\n return out\nend tell`, 'scan');\n return splitRecords(raw).map(record => {\n const parts = splitFields(record);\n return {\n id: field(parts, 0),\n subject: field(parts, 1).trim(),\n senderName: field(parts, 2),\n senderEmail: field(parts, 3),\n receivedTime: field(parts, 4),\n isNonDeliveryReport: boolField(parts, 5),\n };\n });\n}\n\n/** The plain-text bodies of the given messages, by id. */\nasync function readBodies(ids: readonly string[]): Promise<Map<string, string>> {\n const bodies = new Map<string, string>();\n if (ids.length === 0) return bodies;\n const raw = await runOsaScript(`tell application \"Microsoft Outlook\"\n set out to \"\"\n repeat with theId in ${asIdList(ids)}\n${messageByIdSnippet(' ')}\n if theMsg is not missing value then\n${bodyTextSnippet(' ')}\n set out to out & ${asRow(['(theId as string)', 'bodyText'])}\n end if\n end repeat\n return out\nend tell`, 'scan');\n for (const record of splitRecords(raw)) {\n const parts = splitFields(record);\n bodies.set(field(parts, 0), field(parts, 1));\n }\n return bodies;\n}\n\n/** Classify an index, then fill each match's failed recipients from its body. */\nasync function collectBounces(indexed: readonly IndexedMessage[], accountAddress: string): Promise<UndeliverableEmail[]> {\n const matched = indexed\n .map(message => ({ message, reason: bounceReason(message) }))\n .filter(entry => entry.reason !== '');\n if (matched.length === 0) return [];\n const bodies = await readBodies(matched.map(entry => entry.message.id));\n return matched.map(({ message, reason }) => ({\n entryId: message.id,\n subject: message.subject,\n senderName: message.senderName,\n senderEmail: message.senderEmail,\n receivedTime: message.receivedTime,\n matchedReason: reason,\n failedRecipients: failedRecipients(bodies.get(message.id) || '', accountAddress),\n }));\n}\n\n/**\n * Find bounce-backs in the Inbox and, unless dry-running, move each to Deleted\n * Items. The same three signals Windows uses apply, in the same order: a report\n * that declares itself one in its headers, then the sender fingerprint, then the\n * subject phrases.\n */\nexport async function cleanUndeliverableEmails(request: CleanUndeliverableRequest): Promise<CleanUndeliverableResult> {\n const matched = await collectBounces(await indexFolder(request.account, 'inbox', request.daysBack), request.account);\n let deletedCount = 0;\n let failed: ItemFailure[] = [];\n if (!request.dryRun && matched.length > 0) {\n const batch = splitBatch(await runOsaScript(`tell application \"Microsoft Outlook\"\n set deletedCount to 0\n set out to \"\"\n repeat with theId in ${asIdList(matched.map(m => m.entryId))}\n set subj to \"\"\n try\n${messageByIdSnippet(' ')}\n if theMsg is missing value then error \"message is no longer in the mailbox\"\n try\n set subj to (subject of theMsg) as string\n end try\n delete theMsg\n set deletedCount to deletedCount + 1\n on error errText\n set out to out & ${ITEM_FAILURE_ROW}\n end try\n end repeat\n return ${asRow(['(deletedCount as string)'])} & out\nend tell`, 'scan'));\n deletedCount = intField(batch.summary, 0);\n failed = batch.failed;\n }\n return {\n account: request.account,\n scannedDays: request.daysBack,\n dryRun: request.dryRun,\n matchedCount: matched.length,\n deletedCount,\n matched,\n failed,\n };\n}\n\n/** The deduplicated addresses bounce-backs report as failed. Never deletes anything. */\nexport async function collectBouncedRecipients(request: CollectBouncesRequest): Promise<string[]> {\n const terms = request.includeDeletedItems ? ['inbox', 'deleted items'] : ['inbox'];\n const found = new Set<string>();\n for (const term of terms) {\n const bounces = await collectBounces(await indexFolder(request.account, term, request.daysBack), request.account);\n for (const bounce of bounces) {\n for (const address of bounce.failedRecipients) found.add(address);\n }\n }\n return [...found];\n}\n\n/**\n * Sent Items within the window, each message with every address it went to,\n * newest first. Indexed on `time sent` — outgoing mail's own stamp; indexing on\n * `time received` yields an empty set that looks exactly like an empty folder.\n */\nexport async function readSentRecipientGroups(request: SentGroupsRequest): Promise<SentRecipientGroup[]> {\n const acct = await resolveMacAccount(request.account);\n const indexed = splitRecords(await runOsaScript(`tell application \"Microsoft Outlook\"\n${accountLookupSnippet(acct)}\n${rootFolderSnippet(acct, 'sent items', 'sentFolder')}\n set idList to id of every message of sentFolder\n try\n set timeList to time sent of every message of sentFolder\n on error\n set timeList to {}\n end try\n set subjList to subject of every message of sentFolder\n set hasTimes to ((count of timeList) is (count of idList))\n set cutoff to (current date) - (${asInt(request.daysBack)} * days)\n set out to \"\"\n repeat with i from 1 to (count of idList)\n set stamp to missing value\n if hasTimes then set stamp to item i of timeList\n if stamp is missing value or stamp is greater than or equal to cutoff then\n set out to out & ${asRow(['(item i of idList as string)', '(item i of subjList)', 'my isoDate(stamp)'])}\n end if\n end repeat\n return out\nend tell`, 'scan')).map(record => {\n const parts = splitFields(record);\n return { id: field(parts, 0), subject: field(parts, 1).trim(), sentOn: field(parts, 2) };\n });\n indexed.sort((a, b) => b.sentOn.localeCompare(a.sentOn));\n const chosen = indexed.slice(0, request.limit);\n if (chosen.length === 0) return [];\n\n // Pass 2 — recipients, which can only be read message by message.\n const recipientsById = 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(row => row.id))}\n${messageByIdSnippet(' ')}\n if theMsg is not missing value then\n${allRecipientsSnippet(' ')}\n set out to out & ${asRow(['(theId as string)', 'my sanitizeList(addrList)'])}\n end if\n end repeat\n return out\nend tell`, 'scan');\n for (const record of splitRecords(raw)) {\n const parts = splitFields(record);\n recipientsById.set(field(parts, 0), splitList(field(parts, 1)).map(address => address.toLowerCase()));\n }\n return chosen\n .map(row => ({\n entryId: row.id,\n subject: row.subject,\n sentOn: row.sentOn,\n recipients: recipientsById.get(row.id) ?? [],\n }))\n .filter(group => group.recipients.length > 0);\n}\n"]}
|
package/dist/mac/cleanup.d.ts
CHANGED
|
@@ -2,9 +2,17 @@ import type { DeleteEmailsRequest, PurgeRequest } from '../backend';
|
|
|
2
2
|
import type { DeleteMailResult, PurgeDeletedItemsResult } from '../types';
|
|
3
3
|
/**
|
|
4
4
|
* Delete mail by message id from anywhere in the account, into Deleted Items.
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
5
|
+
* The same four things hold the line as on Windows:
|
|
6
|
+
* - An id that resolves outside this account is refused, whatever
|
|
7
|
+
* `allowProtected` says. A message id resolves against the whole
|
|
8
|
+
* application, and the Inbox/Sent check below only knows this account's
|
|
9
|
+
* folders by name, so another mailbox's mail would otherwise pass it.
|
|
10
|
+
* - Inbox and Sent Items (and their subfolders) are refused unless
|
|
11
|
+
* `allowProtected`.
|
|
12
|
+
* - `dryRun` reports without deleting.
|
|
13
|
+
* - Every outcome names the subject and folder of the item that actually
|
|
14
|
+
* resolved — the folder only once the item is known to be this account's,
|
|
15
|
+
* since the path is built from this account's address.
|
|
8
16
|
*/
|
|
9
17
|
export declare function deleteOutlookEmails(request: DeleteEmailsRequest): Promise<DeleteMailResult>;
|
|
10
18
|
/**
|