@dawnlit/outlook-bridge 2.0.0 → 2.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (99) hide show
  1. package/README.md +16 -9
  2. package/dist/backend.d.ts +2 -2
  3. package/dist/backend.js.map +1 -1
  4. package/dist/bridge.js +3 -3
  5. package/dist/bridge.js.map +1 -1
  6. package/dist/errors.d.ts +17 -8
  7. package/dist/errors.js +17 -13
  8. package/dist/errors.js.map +1 -1
  9. package/dist/index.d.ts +3 -3
  10. package/dist/index.js +3 -2
  11. package/dist/index.js.map +1 -1
  12. package/dist/mac/accounts.d.ts +4 -0
  13. package/dist/mac/accounts.js +10 -0
  14. package/dist/mac/accounts.js.map +1 -1
  15. package/dist/mac/attachments.js +1 -1
  16. package/dist/mac/attachments.js.map +1 -1
  17. package/dist/mac/bounces.js +18 -31
  18. package/dist/mac/bounces.js.map +1 -1
  19. package/dist/mac/cleanup.d.ts +11 -3
  20. package/dist/mac/cleanup.js +30 -58
  21. package/dist/mac/cleanup.js.map +1 -1
  22. package/dist/mac/drafts.d.ts +3 -1
  23. package/dist/mac/drafts.js +37 -49
  24. package/dist/mac/drafts.js.map +1 -1
  25. package/dist/mac/folders.d.ts +10 -3
  26. package/dist/mac/folders.js +31 -23
  27. package/dist/mac/folders.js.map +1 -1
  28. package/dist/mac/profile.d.ts +12 -0
  29. package/dist/mac/profile.js +75 -1
  30. package/dist/mac/profile.js.map +1 -1
  31. package/dist/mac/read.d.ts +4 -0
  32. package/dist/mac/read.js +68 -42
  33. package/dist/mac/read.js.map +1 -1
  34. package/dist/mac/run.d.ts +19 -5
  35. package/dist/mac/run.js +49 -9
  36. package/dist/mac/run.js.map +1 -1
  37. package/dist/mac/scripts.d.ts +67 -14
  38. package/dist/mac/scripts.js +150 -31
  39. package/dist/mac/scripts.js.map +1 -1
  40. package/dist/mac/send.d.ts +5 -4
  41. package/dist/mac/send.js +52 -14
  42. package/dist/mac/send.js.map +1 -1
  43. package/dist/mac/templates.js +16 -22
  44. package/dist/mac/templates.js.map +1 -1
  45. package/dist/mail.d.ts +11 -3
  46. package/dist/mail.js +25 -6
  47. package/dist/mail.js.map +1 -1
  48. package/dist/runtime.js.map +1 -1
  49. package/dist/shared/args.js.map +1 -1
  50. package/dist/shared/attachmentMatch.d.ts +0 -6
  51. package/dist/shared/attachmentMatch.js +0 -1
  52. package/dist/shared/attachmentMatch.js.map +1 -1
  53. package/dist/shared/exec.js.map +1 -1
  54. package/dist/shared/locate.d.ts +0 -8
  55. package/dist/shared/locate.js +0 -1
  56. package/dist/shared/locate.js.map +1 -1
  57. package/dist/shared/replyBody.d.ts +0 -15
  58. package/dist/shared/replyBody.js +0 -2
  59. package/dist/shared/replyBody.js.map +1 -1
  60. package/dist/shared/templateLimits.d.ts +14 -0
  61. package/dist/shared/templateLimits.js +21 -0
  62. package/dist/shared/templateLimits.js.map +1 -0
  63. package/dist/templateBody.d.ts +6 -0
  64. package/dist/templateBody.js +8 -1
  65. package/dist/templateBody.js.map +1 -1
  66. package/dist/types.d.ts +43 -7
  67. package/dist/types.js.map +1 -1
  68. package/dist/windows/accounts.js +1 -1
  69. package/dist/windows/accounts.js.map +1 -1
  70. package/dist/windows/attachments.js +2 -2
  71. package/dist/windows/attachments.js.map +1 -1
  72. package/dist/windows/bounces.js +7 -7
  73. package/dist/windows/bounces.js.map +1 -1
  74. package/dist/windows/cleanup.d.ts +5 -1
  75. package/dist/windows/cleanup.js +18 -15
  76. package/dist/windows/cleanup.js.map +1 -1
  77. package/dist/windows/drafts.d.ts +4 -4
  78. package/dist/windows/drafts.js +22 -33
  79. package/dist/windows/drafts.js.map +1 -1
  80. package/dist/windows/folders.js +2 -2
  81. package/dist/windows/folders.js.map +1 -1
  82. package/dist/{shared → windows}/json.d.ts +11 -6
  83. package/dist/{shared → windows}/json.js +12 -7
  84. package/dist/windows/json.js.map +1 -0
  85. package/dist/windows/read.js +23 -16
  86. package/dist/windows/read.js.map +1 -1
  87. package/dist/windows/run.d.ts +0 -2
  88. package/dist/windows/run.js +1 -2
  89. package/dist/windows/run.js.map +1 -1
  90. package/dist/windows/scripts.d.ts +10 -4
  91. package/dist/windows/scripts.js +21 -12
  92. package/dist/windows/scripts.js.map +1 -1
  93. package/dist/windows/send.d.ts +13 -4
  94. package/dist/windows/send.js +24 -6
  95. package/dist/windows/send.js.map +1 -1
  96. package/dist/windows/templates.js +11 -17
  97. package/dist/windows/templates.js.map +1 -1
  98. package/package.json +2 -2
  99. package/dist/shared/json.js.map +0 -1
@@ -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/** Remove any failure tags from a message meant for display — e.g. a per-item error. */\nexport function stripFailureTags(message: string): string {\n return message.replace(new RegExp(FAILURE_TAG.source, 'g'), '').trim();\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
@@ -1,11 +1,11 @@
1
- export declare const getOutlookAccounts: () => Promise<string[]>, sendOutlookEmail: (params: import("./types").SendEmailParams) => Promise<void>, replyOutlookEmail: (params: import("./types").ReplyEmailParams) => Promise<import("./types").ReplyEmailResult>, readInboxEmails: (emailAccount: string, options?: import("./types").ReadInboxOptions) => Promise<import("./types").InboxEmail[]>, searchInboxByFilter: (emailAccount: string, filter?: import("./types").InboxSearchFilter) => Promise<import("./types").InboxSearchMatch[]>, readSelectedEmail: () => Promise<import("./types").SelectedEmail>, readEmailBody: (email: import("./types").EmailRef, options?: import("./types").ReadEmailBodyOptions) => Promise<import("./types").EmailBodyResult>, openOutlookEmail: (email: import("./types").EmailRef) => Promise<void>, locateEmail: (description: import("./types").EmailDescription, options?: import("./types").LocateEmailOptions) => Promise<import("./types").InboxSearchMatch | null>, listInboxFolders: (emailAccount: string, options?: import("./types").ListFoldersOptions) => Promise<import("./types").InboxFolderInfo[]>, moveOutlookEmails: (emailAccount: string, entryIds: readonly string[], folder: string, options?: import("./types").MoveEmailsOptions) => Promise<import("./types").MoveEmailsResult>, listOutlookDrafts: (emailAccount: string, options?: import("./types").ListDraftsOptions) => Promise<import("./types").ListDraftsResult>, sendDrafts: (emailAccount: string, entryIds: readonly string[]) => Promise<import("./types").SendDraftsResult>, sendAllDrafts: (emailAccount: string) => Promise<import("./types").SendDraftsResult>, deleteOutlookDrafts: (emailAccount: string, entryIds: readonly string[]) => Promise<import("./types").DeleteDraftsResult>, deleteOutlookEmails: (emailAccount: string, entryIds: readonly string[], options?: import("./types").DeleteMailOptions) => Promise<import("./types").DeleteMailResult>, purgeDeletedItems: (emailAccount: string, options?: import("./types").PurgeDeletedItemsOptions) => Promise<import("./types").PurgeDeletedItemsResult>, saveEmailAttachment: (email: import("./types").EmailRef, fileName: string, options?: import("./types").SaveAttachmentOptions) => Promise<import("./types").SavedAttachment>, saveEmailAttachments: (email: import("./types").EmailRef, fileNames: readonly string[], options?: import("./types").SaveAttachmentOptions) => Promise<import("./types").SavedAttachment[]>, cleanUndeliverableEmails: (emailAccount: string, options?: import("./types").CleanUndeliverableOptions) => Promise<import("./types").CleanUndeliverableResult>, collectBouncedRecipients: (emailAccount: string, options?: import("./types").CollectBouncedRecipientsOptions) => Promise<string[]>, readSentRecipientGroups: (emailAccount: string, options?: import("./types").SentRecipientGroupsOptions) => Promise<import("./types").SentRecipientGroup[]>, readBounceReport: (emailAccount: string, options?: import("./types").BounceReportOptions) => Promise<import("./types").BounceReport>, listOutlookSignatures: () => Promise<string[]>, readOutlookSignatureHtml: (name: string) => Promise<string>, readTemplateEmails: (emailAccount: string, options?: import("./types").ReadTemplatesOptions) => Promise<import("./types").TemplateFolderResult>, saveTemplateEmail: (emailAccount: string, template: import("./types").SaveTemplateParams) => Promise<import("./types").SaveTemplateResult>, editEmailTemplate: (label: string, currentHtml: string) => Promise<string>;
1
+ export declare const getOutlookAccounts: () => Promise<string[]>, sendOutlookEmail: (params: import("./types").SendEmailParams) => Promise<import("./types").SendEmailResult>, replyOutlookEmail: (params: import("./types").ReplyEmailParams) => Promise<import("./types").ReplyEmailResult>, readInboxEmails: (emailAccount: string, options?: import("./types").ReadInboxOptions) => Promise<import("./types").InboxEmail[]>, searchInboxByFilter: (emailAccount: string, filter?: import("./types").InboxSearchFilter) => Promise<import("./types").InboxSearchMatch[]>, readSelectedEmail: () => Promise<import("./types").SelectedEmail>, readEmailBody: (email: import("./types").EmailRef, options?: import("./types").ReadEmailBodyOptions) => Promise<import("./types").EmailBodyResult>, openOutlookEmail: (email: import("./types").EmailRef) => Promise<void>, locateEmail: (description: import("./types").EmailDescription, options?: import("./types").LocateEmailOptions) => Promise<import("./types").InboxSearchMatch | null>, listInboxFolders: (emailAccount: string, options?: import("./types").ListFoldersOptions) => Promise<import("./types").InboxFolderInfo[]>, moveOutlookEmails: (emailAccount: string, entryIds: readonly string[], folder: string, options?: import("./types").MoveEmailsOptions) => Promise<import("./types").MoveEmailsResult>, listOutlookDrafts: (emailAccount: string, options?: import("./types").ListDraftsOptions) => Promise<import("./types").ListDraftsResult>, sendDrafts: (emailAccount: string, entryIds: readonly string[]) => Promise<import("./types").SendDraftsResult>, sendAllDrafts: (emailAccount: string) => Promise<import("./types").SendDraftsResult>, deleteOutlookDrafts: (emailAccount: string, entryIds: readonly string[]) => Promise<import("./types").DeleteDraftsResult>, deleteOutlookEmails: (emailAccount: string, entryIds: readonly string[], options?: import("./types").DeleteMailOptions) => Promise<import("./types").DeleteMailResult>, purgeDeletedItems: (emailAccount: string, options?: import("./types").PurgeDeletedItemsOptions) => Promise<import("./types").PurgeDeletedItemsResult>, saveEmailAttachment: (email: import("./types").EmailRef, fileName: string, options?: import("./types").SaveAttachmentOptions) => Promise<import("./types").SavedAttachment>, saveEmailAttachments: (email: import("./types").EmailRef, fileNames: readonly string[], options?: import("./types").SaveAttachmentOptions) => Promise<import("./types").SavedAttachment[]>, cleanUndeliverableEmails: (emailAccount: string, options?: import("./types").CleanUndeliverableOptions) => Promise<import("./types").CleanUndeliverableResult>, collectBouncedRecipients: (emailAccount: string, options?: import("./types").CollectBouncedRecipientsOptions) => Promise<string[]>, readSentRecipientGroups: (emailAccount: string, options?: import("./types").SentRecipientGroupsOptions) => Promise<import("./types").SentRecipientGroup[]>, readBounceReport: (emailAccount: string, options?: import("./types").BounceReportOptions) => Promise<import("./types").BounceReport>, listOutlookSignatures: () => Promise<string[]>, readOutlookSignatureHtml: (name: string) => Promise<string>, readTemplateEmails: (emailAccount: string, options?: import("./types").ReadTemplatesOptions) => Promise<import("./types").TemplateFolderResult>, saveTemplateEmail: (emailAccount: string, template: import("./types").SaveTemplateParams) => Promise<import("./types").SaveTemplateResult>, editEmailTemplate: (label: string, currentHtml: string) => Promise<string>;
2
2
  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';
10
10
  export type { ComposeOptions, TemplateMarkers } from './templateBody';
11
- export type { OutlookBridge, BridgeCapability, CapabilityMap, EmailLocator, EmailRef, Recipients, SendEmailParams, ReplyEmailParams, ReplyEmailResult, ItemFailure, ReadInboxOptions, InboxEmail, InboxSearchFilter, InboxSearchMatch, EmailDescription, LocateEmailOptions, SelectedEmail, ReadEmailBodyOptions, EmailBodyResult, MailFolderRef, ListFoldersOptions, InboxFolderInfo, MoveEmailsOptions, MoveEmailsResult, ListDraftsOptions, ListDraftsResult, OutlookDraft, SendDraftsResult, DeleteDraftsResult, DeleteMailOptions, DeleteMailOutcome, DeleteMailResult, PurgeDeletedItemsOptions, PurgeDeletedItemsResult, SaveAttachmentOptions, SavedAttachment, CleanUndeliverableOptions, CleanUndeliverableResult, UndeliverableEmail, CollectBouncedRecipientsOptions, SentRecipientGroupsOptions, SentRecipientGroup, BounceReportOptions, BounceReport, BouncedSend, ReadTemplatesOptions, TemplateEmail, TemplateFolderResult, SaveTemplateParams, SaveTemplateResult, } from './types';
11
+ export type { OutlookBridge, BridgeCapability, CapabilityMap, EmailLocator, EmailRef, Recipients, SendEmailParams, SendEmailResult, ReplyEmailParams, ReplyEmailResult, ItemFailure, ReadInboxOptions, InboxEmail, InboxSearchFilter, InboxSearchMatch, EmailDescription, LocateEmailOptions, SelectedEmail, ReadEmailBodyOptions, EmailBodyResult, MailFolderRef, ListFoldersOptions, InboxFolderInfo, MoveEmailsOptions, MoveEmailsResult, ListDraftsOptions, ListDraftsResult, OutlookDraft, SendDraftsResult, DeleteDraftsResult, DeleteMailOptions, DeleteMailOutcome, DeleteMailResult, PurgeDeletedItemsOptions, PurgeDeletedItemsResult, SaveAttachmentOptions, SavedAttachment, CleanUndeliverableOptions, CleanUndeliverableResult, UndeliverableEmail, CollectBouncedRecipientsOptions, SentRecipientGroupsOptions, SentRecipientGroup, BounceReportOptions, BounceReport, BouncedSend, ReadTemplatesOptions, TemplateEmail, TemplateFolderResult, SaveTemplateParams, SaveTemplateResult, } from './types';
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.findTokens = 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.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 = 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,mCAWkB;AAVd,sGAAA,YAAY,OAAA;AACZ,kHAAA,wBAAwB,OAAA;AACxB,6GAAA,mBAAmB,OAAA;AACnB,8GAAA,oBAAoB,OAAA;AACpB,uGAAA,aAAa,OAAA;AACb,6GAAA,mBAAmB,OAAA;AACnB,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 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 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"]}
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"]}
@@ -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[]>;
@@ -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
@@ -1 +1 @@
1
- {"version":3,"file":"accounts.js","sourceRoot":"","sources":["../../src/mac/accounts.ts"],"names":[],"mappings":";;AAcA,gDAeC;AA7BD,2CAA2C;AAC3C,+BAAqC;AACrC,uCAAgD;AAChD,uCAAkD;AAElD;;;;;;;;GAQG;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,EAAC,YAAY,EAAC,IAAI,OAAO,EAAE,CAAC;QACnC,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,OAAO,QAAQ,CAAC;AACpB,CAAC","sourcesContent":["// The mailboxes Outlook for Mac can reach.\nimport { runOsaScript } from './run';\nimport { readProfileAccounts } from './profile';\nimport { LIST_ACCOUNTS_SNIPPET } from './scripts';\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 */\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 return accounts;\n}\n"]}
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"]}
@@ -33,7 +33,7 @@ end tell`, 'quick'));
33
33
  senderEmail: (0, run_1.field)(parts, scripts_1.MessageDetail.senderEmail),
34
34
  receivedTime: (0, run_1.field)(parts, scripts_1.MessageDetail.receivedTime),
35
35
  };
36
- const attachmentNames = (0, run_1.splitList)((0, run_1.field)(parts, scripts_1.MessageDetail.attachmentNames));
36
+ const attachmentNames = (0, run_1.splitPositionalList)((0, run_1.field)(parts, scripts_1.MessageDetail.attachmentNames));
37
37
  const picks = request.fileNames.map(fileName => {
38
38
  const index = (0, attachmentMatch_1.findAttachmentIndex)(attachmentNames, fileName);
39
39
  if (index === -1) {
@@ -1 +1 @@
1
- {"version":3,"file":"attachments.js","sourceRoot":"","sources":["../../src/mac/attachments.ts"],"names":[],"mappings":"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","sourcesContent":["// Saving attachments off an email.\n//\n// Two runs: the first reads the email's identity and attachment names, the\n// matching and file naming happen in TypeScript against shared/attachmentMatch,\n// and the second run saves what matched. That keeps one definition of \"does this\n// name match\", one wording for the error, and one file-naming rule.\nimport fs from 'fs';\nimport { asInt, asRow, asString, field, runOsaScript, splitList, summaryFields } from './run';\nimport {\n macMessageId,\n MessageDetail,\n messageDetailFields,\n messageDetailSnippet,\n messageLookupSnippet\n} from './scripts';\nimport {\n attachmentNotFoundMessage,\n findAttachmentIndex,\n safeFileName,\n uniqueSavePath\n} from '../shared/attachmentMatch';\nimport { NotFoundError } from '../errors';\nimport type { SaveAttachmentsRequest } from '../backend';\nimport type { SavedAttachment } from '../types';\n\n/**\n * Save the named attachments of one email, in the order named. Every name is\n * matched before anything is written, so a batch with one bad name fails\n * without having saved half its files.\n */\nexport async function saveEmailAttachments(request: SaveAttachmentsRequest): Promise<SavedAttachment[]> {\n const id = macMessageId(request.email.entryId);\n const parts = summaryFields(await runOsaScript(`tell application \"Microsoft Outlook\"\n${messageLookupSnippet(id)}\n${messageDetailSnippet()}\n return ${asRow(messageDetailFields())}\nend tell`, 'quick'));\n const email = {\n subject: field(parts, MessageDetail.subject).trim(),\n senderName: field(parts, MessageDetail.senderName),\n senderEmail: field(parts, MessageDetail.senderEmail),\n receivedTime: field(parts, MessageDetail.receivedTime),\n };\n const attachmentNames = splitList(field(parts, MessageDetail.attachmentNames));\n\n const picks = request.fileNames.map(fileName => {\n const index = findAttachmentIndex(attachmentNames, fileName);\n if (index === -1) {\n throw new NotFoundError('attachment', attachmentNotFoundMessage({\n fileName,\n ...email,\n present: attachmentNames.join(', ') || '(none)',\n }));\n }\n return {index, fileName: attachmentNames[index]};\n });\n\n // Paths are claimed as they are chosen, so two picks with one name don't collide.\n const claimed = new Set<string>();\n const saves = picks.map(pick => {\n const file = uniqueSavePath(request.destDir, safeFileName(pick.fileName), path => claimed.has(path) || fs.existsSync(path));\n claimed.add(file);\n return {...pick, path: file};\n });\n await runOsaScript(`tell application \"Microsoft Outlook\"\n${messageLookupSnippet(id)}\n${saves.map(save => ` save (item ${asInt(save.index + 1)} of attachments of theMsg) in POSIX file ${asString(save.path)}`).join('\\n')}\nend tell`, 'standard');\n return saves.map(save => ({path: save.path, fileName: save.fileName, ...email}));\n}\n"]}
1
+ {"version":3,"file":"attachments.js","sourceRoot":"","sources":["../../src/mac/attachments.ts"],"names":[],"mappings":"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","sourcesContent":["// Saving attachments off an email.\n//\n// Two runs: the first reads the email's identity and attachment names, the\n// matching and file naming happen in TypeScript against shared/attachmentMatch,\n// and the second run saves what matched. That keeps one definition of \"does this\n// name match\", one wording for the error, and one file-naming rule.\nimport fs from 'fs';\nimport { asInt, asRow, asString, field, runOsaScript, splitPositionalList, summaryFields } from './run';\nimport {\n macMessageId,\n MessageDetail,\n messageDetailFields,\n messageDetailSnippet,\n messageLookupSnippet,\n} from './scripts';\nimport {\n attachmentNotFoundMessage,\n findAttachmentIndex,\n safeFileName,\n uniqueSavePath,\n} from '../shared/attachmentMatch';\nimport { NotFoundError } from '../errors';\nimport type { SaveAttachmentsRequest } from '../backend';\nimport type { SavedAttachment } from '../types';\n\n/**\n * Save the named attachments of one email, in the order named. Every name is\n * matched before anything is written, so a batch with one bad name fails\n * without having saved half its files.\n */\nexport async function saveEmailAttachments(request: SaveAttachmentsRequest): Promise<SavedAttachment[]> {\n const id = macMessageId(request.email.entryId);\n const parts = summaryFields(await runOsaScript(`tell application \"Microsoft Outlook\"\n${messageLookupSnippet(id)}\n${messageDetailSnippet()}\n return ${asRow(messageDetailFields())}\nend tell`, 'quick'));\n const email = {\n subject: field(parts, MessageDetail.subject).trim(),\n senderName: field(parts, MessageDetail.senderName),\n senderEmail: field(parts, MessageDetail.senderEmail),\n receivedTime: field(parts, MessageDetail.receivedTime),\n };\n const attachmentNames = splitPositionalList(field(parts, MessageDetail.attachmentNames));\n\n const picks = request.fileNames.map(fileName => {\n const index = findAttachmentIndex(attachmentNames, fileName);\n if (index === -1) {\n throw new NotFoundError('attachment', attachmentNotFoundMessage({\n fileName,\n ...email,\n present: attachmentNames.join(', ') || '(none)',\n }));\n }\n return { index, fileName: attachmentNames[index] };\n });\n\n // Paths are claimed as they are chosen, so two picks with one name don't collide.\n const claimed = new Set<string>();\n const saves = picks.map(pick => {\n const file = uniqueSavePath(request.destDir, safeFileName(pick.fileName), path => claimed.has(path) || fs.existsSync(path));\n claimed.add(file);\n return { ...pick, path: file };\n });\n await runOsaScript(`tell application \"Microsoft Outlook\"\n${messageLookupSnippet(id)}\n${saves.map(save => ` save (item ${asInt(save.index + 1)} of attachments of theMsg) in POSIX file ${asString(save.path)}`).join('\\n')}\nend tell`, 'standard');\n return saves.map(save => ({ path: save.path, fileName: save.fileName, ...email }));\n}\n"]}
@@ -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 (count of timeList) is (count of idList) then set stamp to item i of timeList
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 (count of sndList) is (count of idList) then
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
- set theMsg to missing value
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
- set bodyText to ""
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
@@ -147,18 +143,15 @@ async function collectBounces(indexed, accountAddress) {
147
143
  async function cleanUndeliverableEmails(request) {
148
144
  const matched = await collectBounces(await indexFolder(request.account, 'inbox', request.daysBack), request.account);
149
145
  let deletedCount = 0;
150
- const failed = [];
146
+ let failed = [];
151
147
  if (!request.dryRun && matched.length > 0) {
152
- const raw = await (0, run_1.runOsaScript)(`tell application "Microsoft Outlook"
148
+ const batch = (0, run_1.splitBatch)(await (0, run_1.runOsaScript)(`tell application "Microsoft Outlook"
153
149
  set deletedCount to 0
154
150
  set out to ""
155
151
  repeat with theId in ${(0, run_1.asIdList)(matched.map(m => m.entryId))}
156
152
  set subj to ""
157
153
  try
158
- set theMsg to missing value
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
@@ -166,17 +159,13 @@ async function cleanUndeliverableEmails(request) {
166
159
  delete theMsg
167
160
  set deletedCount to deletedCount + 1
168
161
  on error errText
169
- set out to out & ${(0, run_1.asRow)(['(theId as string)', 'subj', 'errText'])}
162
+ set out to out & ${run_1.ITEM_FAILURE_ROW}
170
163
  end try
171
164
  end repeat
172
165
  return ${(0, run_1.asRow)(['(deletedCount as string)'])} & out
173
- end tell`, 'scan');
174
- const records = (0, run_1.splitRecords)(raw);
175
- deletedCount = (0, run_1.intField)((0, run_1.splitFields)(records[0] ?? ''), 0);
176
- for (const record of records.slice(1)) {
177
- const parts = (0, run_1.splitFields)(record);
178
- failed.push({ entryId: (0, run_1.field)(parts, 0), subject: (0, run_1.field)(parts, 1), error: (0, run_1.field)(parts, 2) });
179
- }
166
+ end tell`, 'scan'));
167
+ deletedCount = (0, run_1.intField)(batch.summary, 0);
168
+ failed = batch.failed;
180
169
  }
181
170
  return {
182
171
  account: request.account,
@@ -218,11 +207,12 @@ ${(0, scripts_1.rootFolderSnippet)(acct, 'sent items', 'sentFolder')}
218
207
  set timeList to {}
219
208
  end try
220
209
  set subjList to subject of every message of sentFolder
210
+ set hasTimes to ((count of timeList) is (count of idList))
221
211
  set cutoff to (current date) - (${(0, run_1.asInt)(request.daysBack)} * days)
222
212
  set out to ""
223
213
  repeat with i from 1 to (count of idList)
224
214
  set stamp to missing value
225
- if (count of timeList) is (count of idList) then set stamp to item i of timeList
215
+ if hasTimes then set stamp to item i of timeList
226
216
  if stamp is missing value or stamp is greater than or equal to cutoff then
227
217
  set out to out & ${(0, run_1.asRow)(['(item i of idList as string)', '(item i of subjList)', 'my isoDate(stamp)'])}
228
218
  end if
@@ -241,12 +231,9 @@ end tell`, 'scan')).map(record => {
241
231
  const raw = await (0, run_1.runOsaScript)(`tell application "Microsoft Outlook"
242
232
  set out to ""
243
233
  repeat with theId in ${(0, run_1.asIdList)(chosen.map(row => row.id))}
244
- set theMsg to missing value
245
- try
246
- set theMsg to message id theId
247
- end try
234
+ ${(0, scripts_1.messageByIdSnippet)(' ')}
248
235
  if theMsg is not missing value then
249
- ${(0, scripts_1.allRecipientsSnippet)('theMsg', ' ')}
236
+ ${(0, scripts_1.allRecipientsSnippet)(' ')}
250
237
  set out to out & ${(0, run_1.asRow)(['(theId as string)', 'my sanitizeList(addrList)'])}
251
238
  end if
252
239
  end repeat
@@ -261,7 +248,7 @@ end tell`, 'scan');
261
248
  entryId: row.id,
262
249
  subject: row.subject,
263
250
  sentOn: row.sentOn,
264
- recipients: recipientsById.get(row.id) ?? []
251
+ recipients: recipientsById.get(row.id) ?? [],
265
252
  }))
266
253
  .filter(group => group.recipients.length > 0);
267
254
  }
@@ -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 runOsaScript,\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 const failed: ItemFailure[] = [];\n if (!request.dryRun && matched.length > 0) {\n const raw = 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 & ${asRow(['(theId as string)', 'subj', 'errText'])}\n end try\n end repeat\n return ${asRow(['(deletedCount as string)'])} & out\nend tell`, 'scan');\n const records = splitRecords(raw);\n deletedCount = intField(splitFields(records[0] ?? ''), 0);\n for (const record of records.slice(1)) {\n const parts = splitFields(record);\n failed.push({entryId: field(parts, 0), subject: field(parts, 1), error: field(parts, 2)});\n }\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('theMsg', ' ')}\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"]}
@@ -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
- * Inbox and Sent Items (and their subfolders) are refused unless
6
- * `allowProtected`; `dryRun` reports without deleting; every outcome names the
7
- * subject and folder of the item that actually resolved.
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
  /**