@braedonsaunders/appkit-mailbox 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +661 -0
- package/README.md +11 -0
- package/index.d.ts +4 -0
- package/index.d.ts.map +1 -0
- package/index.js +3 -0
- package/index.js.map +1 -0
- package/package.json +68 -0
- package/react.d.ts +119 -0
- package/react.d.ts.map +1 -0
- package/react.js +271 -0
- package/react.js.map +1 -0
- package/send.d.ts +10 -0
- package/send.d.ts.map +1 -0
- package/send.js +70 -0
- package/send.js.map +1 -0
- package/sync.d.ts +11 -0
- package/sync.d.ts.map +1 -0
- package/sync.js +112 -0
- package/sync.js.map +1 -0
- package/types.d.ts +98 -0
- package/types.d.ts.map +1 -0
- package/types.js +2 -0
- package/types.js.map +1 -0
package/react.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"react.js","sourceRoot":"","sources":["../src/react.tsx"],"names":[],"mappings":"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","sourcesContent":["'use client'\n\n/**\n * MailboxInbox — the operator surface for the mail engine.\n *\n * The engine in `./index` speaks IMAP/SMTP and owns no state; this component\n * owns no data. Everything it renders arrives as props and every mutation\n * leaves through a callback, so the application keeps the ledger, the folder\n * counts, and the send/reply transport. Only the reply and compose drafts\n * (with their busy/error state) live here, because a draft has no meaning\n * outside the pane that is typing it.\n *\n * The three-pane layout, folder rail, row anatomy, reading pane, empty states,\n * and skeleton follow `@braedonsaunders/appkit-notifications`' `NotificationInbox` so a mail\n * screen and a notification screen read as the same product.\n */\n\nimport * as React from 'react'\nimport {\n ArrowRight,\n ChevronLeft,\n Download,\n Inbox,\n Loader2,\n Mail,\n Mails,\n Menu,\n Paperclip,\n RotateCw,\n Send,\n SquarePen,\n TriangleAlert,\n X,\n} from 'lucide-react'\nimport { Badge, Button, Drawer, Input, Select, Skeleton, Textarea, cn } from '@braedonsaunders/appkit-ui'\n\nexport type MailboxOption = {\n id: string\n ownerName: string\n address: string\n}\n\nexport type MailFolderKey = 'inbox' | 'sent' | 'all'\n\nexport type MailThreadListItem = {\n id: string\n /** The other side of the conversation — one name, or a joined list. */\n counterparty: string\n subject: string\n /** ISO 8601 timestamp of the most recent message in the thread. */\n at: string\n /** The thread is still awaiting a resolution. */\n open: boolean\n unread?: boolean\n}\n\nexport type MailAttachmentView = {\n filename: string\n /** Pre-formatted by the application (\"184 KB\") — this component never guesses units. */\n sizeLabel: string\n href: string\n}\n\nexport type MailMessageView = {\n id: string\n direction: 'inbound' | 'outbound'\n fromLabel: string\n toLabel: string\n /** ISO 8601 timestamp. */\n at: string\n bodyText: string\n attachments: MailAttachmentView[]\n}\n\nexport type MailboxInboxCopy = {\n title: string\n mailbox: string\n searchMailboxes: string\n folders: string\n inbox: string\n sent: string\n all: string\n refresh: string\n newMessage: string\n back: string\n cancel: string\n open: string\n unread: string\n conversation: string\n conversations: string\n messageNoun: string\n messagesNoun: string\n emptyList: string\n emptyListDescription: string\n emptyPane: string\n emptyPaneDescription: string\n noBody: string\n attachments: string\n download: string\n reply: string\n replyPlaceholder: string\n send: string\n sending: string\n to: string\n toPlaceholder: string\n subject: string\n subjectPlaceholder: string\n message: string\n messagePlaceholder: string\n}\n\nconst DEFAULT_COPY: MailboxInboxCopy = {\n title: 'Mail',\n mailbox: 'Mailbox',\n searchMailboxes: 'Search mailboxes',\n folders: 'Folders',\n inbox: 'Inbox',\n sent: 'Sent',\n all: 'All mail',\n refresh: 'Refresh',\n newMessage: 'New message',\n back: 'Back to conversations',\n cancel: 'Cancel',\n open: 'Open thread',\n unread: 'unread',\n conversation: 'conversation',\n conversations: 'conversations',\n messageNoun: 'message',\n messagesNoun: 'messages',\n emptyList: 'No conversations',\n emptyListDescription: 'Messages in this folder will appear here.',\n emptyPane: 'Select a conversation',\n emptyPaneDescription: 'Choose a conversation from the list to read it here.',\n noBody: 'This message has no text body.',\n attachments: 'Attachments',\n download: 'Download',\n reply: 'Reply',\n replyPlaceholder: 'Write a reply…',\n send: 'Send',\n sending: 'Sending…',\n to: 'To',\n toPlaceholder: 'name@example.com',\n subject: 'Subject',\n subjectPlaceholder: 'What is this about?',\n message: 'Message',\n messagePlaceholder: 'Write your message…',\n}\n\nconst FOLDER_ORDER: MailFolderKey[] = ['inbox', 'sent', 'all']\n\nfunction useIsDesktop() {\n const [isDesktop, setIsDesktop] = React.useState(true)\n React.useEffect(() => {\n const media = window.matchMedia('(min-width: 1024px)')\n const sync = () => setIsDesktop(media.matches)\n sync()\n media.addEventListener('change', sync)\n return () => media.removeEventListener('change', sync)\n }, [])\n return isDesktop\n}\n\nfunction relativeTime(iso: string): string {\n const minutes = Math.floor((Date.now() - new Date(iso).getTime()) / 60_000)\n if (minutes < 1) return 'now'\n if (minutes < 60) return `${minutes}m`\n const hours = Math.floor(minutes / 60)\n if (hours < 24) return `${hours}h`\n const days = Math.floor(hours / 24)\n if (days < 7) return `${days}d`\n return new Date(iso).toLocaleDateString(undefined, { month: 'short', day: 'numeric' })\n}\n\nfunction fullDate(iso: string): string {\n return new Date(iso).toLocaleString(undefined, {\n weekday: 'short',\n month: 'short',\n day: 'numeric',\n hour: 'numeric',\n minute: '2-digit',\n })\n}\n\nfunction initials(name: string): string {\n const parts = name\n .replace(/<[^>]*>/g, ' ')\n .split(/[\\s,]+/)\n .map((part) => part.trim())\n .filter(Boolean)\n const first = parts[0]\n if (!first) return '?'\n const last = parts.length > 1 ? parts[parts.length - 1] : undefined\n const letters = `${first[0] ?? ''}${last?.[0] ?? ''}`\n return letters.toUpperCase() || '?'\n}\n\nfunction errorMessage(cause: unknown): string {\n if (cause instanceof Error && cause.message) return cause.message\n if (typeof cause === 'string' && cause) return cause\n return 'The message could not be sent.'\n}\n\nfunction CountPill({ value, active }: { value: number; active?: boolean }) {\n if (value <= 0) return null\n return (\n <span\n className={cn(\n 'ml-auto inline-flex min-w-5 items-center justify-center rounded-full px-1.5 text-[11px] font-semibold tabular-nums',\n active ? 'bg-primary text-primary-fg' : 'bg-bg-subtle text-fg-muted',\n )}\n >\n {value > 99 ? '99+' : value}\n </span>\n )\n}\n\nfunction FolderButton({\n icon,\n label,\n count,\n active,\n onClick,\n}: {\n icon: React.ReactNode\n label: string\n count: number\n active: boolean\n onClick: () => void\n}) {\n return (\n <button\n type=\"button\"\n onClick={onClick}\n aria-current={active ? 'true' : undefined}\n className={cn(\n 'flex w-full items-center gap-2.5 rounded-lg px-2.5 py-2 text-sm transition-colors',\n active\n ? 'bg-primary-subtle font-medium text-fg'\n : 'text-fg-muted hover:bg-surface-hover hover:text-fg',\n )}\n >\n <span className={cn('shrink-0', active ? 'text-primary' : 'text-fg-subtle')}>{icon}</span>\n <span className=\"min-w-0 flex-1 truncate text-left\">{label}</span>\n <CountPill value={count} active={active} />\n </button>\n )\n}\n\nfunction FolderIcon({ folder }: { folder: MailFolderKey }) {\n if (folder === 'inbox') return <Inbox size={16} />\n if (folder === 'sent') return <Send size={16} />\n return <Mails size={16} />\n}\n\nfunction MailboxSwitcher({\n mailboxes,\n activeMailboxId,\n onSwitchMailbox,\n copy,\n className,\n}: {\n mailboxes: MailboxOption[]\n activeMailboxId: string\n onSwitchMailbox: (id: string) => void\n copy: MailboxInboxCopy\n className?: string\n}) {\n return (\n <div className={cn('shrink-0 space-y-1.5', className)}>\n <p className=\"px-0.5 text-[11px] font-semibold uppercase tracking-wide text-fg-subtle\">\n {copy.mailbox}\n </p>\n <Select\n value={activeMailboxId}\n onChange={(event) => onSwitchMailbox(event.currentTarget.value)}\n aria-label={copy.mailbox}\n sheetTitle={copy.mailbox}\n searchPlaceholder={copy.searchMailboxes}\n triggerClassName=\"h-9 text-sm\"\n >\n {mailboxes.map((mailbox) => (\n <option key={mailbox.id} value={mailbox.id}>\n {`${mailbox.ownerName} · ${mailbox.address}`}\n </option>\n ))}\n </Select>\n </div>\n )\n}\n\nfunction FolderRail({\n mailboxes,\n activeMailboxId,\n onSwitchMailbox,\n folder,\n folderCounts,\n onSwitchFolder,\n copy,\n variant,\n className,\n}: {\n mailboxes: MailboxOption[]\n activeMailboxId: string\n onSwitchMailbox?: ((id: string) => void) | undefined\n folder: MailFolderKey\n folderCounts: Partial<Record<MailFolderKey, number>> | undefined\n onSwitchFolder: (key: MailFolderKey) => void\n copy: MailboxInboxCopy\n variant: 'rail' | 'flyout'\n className?: string\n}) {\n const nav = (\n <div className=\"space-y-0.5\">\n {FOLDER_ORDER.map((key) => (\n <FolderButton\n key={key}\n icon={<FolderIcon folder={key} />}\n label={copy[key]}\n count={folderCounts?.[key] ?? 0}\n active={folder === key}\n onClick={() => onSwitchFolder(key)}\n />\n ))}\n </div>\n )\n\n // A control that changes nothing is worse than no control: on a surface that\n // is one mailbox — an inbox opened on the person it belongs to — a picker\n // offering that same mailbox reads as an invitation to go somewhere else.\n // So the switcher appears only where there is somewhere to switch to, which\n // is both a second mailbox and a handler willing to go there.\n const switcher =\n onSwitchMailbox && mailboxes.length > 1 ? (\n <MailboxSwitcher\n mailboxes={mailboxes}\n activeMailboxId={activeMailboxId}\n onSwitchMailbox={onSwitchMailbox}\n copy={copy}\n className={variant === 'flyout' ? 'border-b border-border pb-3' : 'border-b border-border px-3 pb-3'}\n />\n ) : null\n\n if (variant === 'flyout') {\n return (\n <div className=\"flex h-full flex-col\">\n {switcher}\n <div className=\"min-h-0 flex-1 overflow-y-auto pt-2\">{nav}</div>\n </div>\n )\n }\n return (\n <aside className={cn('flex-col border-r border-border bg-surface', className)}>\n <div className=\"flex h-14 shrink-0 items-center gap-2 px-4\">\n <Mail size={18} className=\"text-primary\" />\n <span className=\"text-base font-semibold text-fg\">{copy.title}</span>\n </div>\n {switcher}\n <nav className=\"app-scroll min-h-0 flex-1 overflow-y-auto px-2 py-2\">{nav}</nav>\n </aside>\n )\n}\n\nfunction ThreadRow({\n thread,\n copy,\n selected,\n onOpen,\n}: {\n thread: MailThreadListItem\n copy: MailboxInboxCopy\n selected: boolean\n onOpen: () => void\n}) {\n const unread = thread.unread === true\n return (\n <div\n role=\"button\"\n tabIndex={0}\n onClick={onOpen}\n onKeyDown={(event) => {\n if (event.key === 'Enter' || event.key === ' ') {\n event.preventDefault()\n onOpen()\n }\n }}\n className={cn(\n 'group relative flex cursor-pointer gap-3 border-b border-border-subtle px-3 py-2.5 transition-colors sm:px-4',\n selected ? 'bg-primary-subtle/70' : 'hover:bg-surface-hover',\n )}\n >\n {unread ? <span className=\"absolute inset-y-0 left-0 w-[3px] bg-primary\" aria-hidden /> : null}\n <span\n aria-hidden\n className={cn(\n 'flex size-9 shrink-0 items-center justify-center rounded-full text-xs font-semibold',\n unread ? 'bg-primary-subtle text-primary' : 'bg-bg-subtle text-fg-muted',\n )}\n >\n {initials(thread.counterparty)}\n </span>\n <div className=\"min-w-0 flex-1\">\n <div className=\"flex items-center gap-2\">\n <span className={cn('min-w-0 truncate text-sm', unread ? 'font-semibold text-fg' : 'text-fg')}>\n {thread.counterparty}\n </span>\n {thread.open ? (\n <span\n title={copy.open}\n aria-label={copy.open}\n role=\"img\"\n className=\"size-1.5 shrink-0 rounded-full bg-success\"\n />\n ) : null}\n <time\n suppressHydrationWarning\n dateTime={thread.at}\n className=\"ml-auto shrink-0 text-[11px] whitespace-nowrap text-fg-subtle\"\n >\n {relativeTime(thread.at)}\n </time>\n </div>\n <p className={cn('truncate text-sm', unread ? 'font-medium text-fg' : 'text-fg-muted')}>\n {thread.subject}\n </p>\n </div>\n </div>\n )\n}\n\nfunction AttachmentChip({ attachment, copy }: { attachment: MailAttachmentView; copy: MailboxInboxCopy }) {\n return (\n <a\n href={attachment.href}\n download={attachment.filename}\n title={`${copy.download}: ${attachment.filename}`}\n className=\"group/chip inline-flex max-w-full items-center gap-2 rounded-lg border border-border bg-surface px-2.5 py-1.5 text-xs transition-colors hover:border-primary/40 hover:bg-surface-hover\"\n >\n <Paperclip size={13} className=\"shrink-0 text-fg-subtle\" aria-hidden />\n <span className=\"min-w-0 truncate font-medium text-fg\">{attachment.filename}</span>\n <span className=\"shrink-0 tabular-nums text-fg-subtle\">{attachment.sizeLabel}</span>\n <Download\n size={13}\n aria-hidden\n className=\"shrink-0 text-fg-subtle transition-colors group-hover/chip:text-primary\"\n />\n </a>\n )\n}\n\nfunction MessageCard({ message, copy }: { message: MailMessageView; copy: MailboxInboxCopy }) {\n const outbound = message.direction === 'outbound'\n return (\n <article\n className={cn(\n 'max-w-[52rem] rounded-xl border px-4 py-3 shadow-sm',\n outbound ? 'ml-auto border-primary/25 bg-primary-subtle/50' : 'mr-auto border-border bg-bg-subtle',\n )}\n >\n <div className=\"flex flex-wrap items-center gap-x-2 gap-y-1 text-xs\">\n <span className=\"min-w-0 truncate font-semibold text-fg\">{message.fromLabel}</span>\n <ArrowRight size={12} className=\"shrink-0 text-fg-subtle\" aria-hidden />\n <span className=\"min-w-0 truncate text-fg-muted\">{message.toLabel}</span>\n <time\n suppressHydrationWarning\n dateTime={message.at}\n className=\"ml-auto shrink-0 text-[11px] whitespace-nowrap text-fg-subtle\"\n >\n {fullDate(message.at)}\n </time>\n </div>\n {message.bodyText.trim() ? (\n <p className=\"mt-2 text-sm leading-relaxed whitespace-pre-wrap text-fg\">{message.bodyText}</p>\n ) : (\n <p className=\"mt-2 text-sm text-fg-subtle\">{copy.noBody}</p>\n )}\n {message.attachments.length ? (\n <div className=\"mt-3 border-t border-border-subtle pt-2.5\">\n <p className=\"mb-1.5 text-[11px] font-semibold uppercase tracking-wide text-fg-subtle\">\n {copy.attachments}\n </p>\n <div className=\"flex flex-wrap gap-1.5\">\n {message.attachments.map((attachment) => (\n <AttachmentChip key={`${attachment.filename}:${attachment.href}`} attachment={attachment} copy={copy} />\n ))}\n </div>\n </div>\n ) : null}\n </article>\n )\n}\n\nfunction InlineError({ message }: { message: string }) {\n return (\n <p role=\"alert\" className=\"flex items-start gap-1.5 text-xs text-danger\">\n <TriangleAlert size={13} className=\"mt-0.5 shrink-0\" aria-hidden />\n <span className=\"min-w-0\">{message}</span>\n </p>\n )\n}\n\nfunction ReplyComposer({\n copy,\n replyLabel,\n onReply,\n}: {\n copy: MailboxInboxCopy\n replyLabel: string | undefined\n onReply: (text: string) => Promise<void>\n}) {\n const fieldId = React.useId()\n const [text, setText] = React.useState('')\n const [busy, setBusy] = React.useState(false)\n const [error, setError] = React.useState<string | null>(null)\n\n const submit = async (event: React.FormEvent<HTMLFormElement>) => {\n event.preventDefault()\n const value = text.trim()\n if (!value || busy) return\n setBusy(true)\n setError(null)\n try {\n await onReply(value)\n setText('')\n } catch (cause) {\n setError(errorMessage(cause))\n } finally {\n setBusy(false)\n }\n }\n\n return (\n <form\n onSubmit={(event) => void submit(event)}\n className=\"shrink-0 space-y-2 border-t border-border bg-surface px-4 py-3 sm:px-6 sm:py-4\"\n >\n <label htmlFor={fieldId} className=\"sr-only\">\n {replyLabel ?? copy.reply}\n </label>\n <Textarea\n id={fieldId}\n value={text}\n onChange={(event) => setText(event.currentTarget.value)}\n placeholder={copy.replyPlaceholder}\n rows={3}\n disabled={busy}\n className=\"min-h-20 resize-y\"\n />\n {error ? <InlineError message={error} /> : null}\n <div className=\"flex items-center gap-2\">\n <span className=\"min-w-0 flex-1 truncate text-xs text-fg-subtle\">{replyLabel ?? copy.reply}</span>\n <Button type=\"submit\" size=\"sm\" disabled={busy || !text.trim()}>\n {busy ? <Loader2 size={14} className=\"animate-spin\" /> : <Send size={14} />}\n {busy ? copy.sending : copy.send}\n </Button>\n </div>\n </form>\n )\n}\n\nfunction ConversationPane({\n conversation,\n copy,\n replyLabel,\n threadKey,\n onReply,\n onClose,\n}: {\n conversation: { subject: string; messages: MailMessageView[] } | null\n copy: MailboxInboxCopy\n replyLabel: string | undefined\n threadKey: string | null\n onReply: (text: string) => Promise<void>\n onClose?: () => void\n}) {\n if (!conversation) {\n return (\n <div className=\"flex h-full flex-col items-center justify-center gap-3 px-6 text-center\">\n <div className=\"flex size-16 items-center justify-center rounded-full bg-bg-subtle\">\n <Mail size={28} className=\"text-fg-subtle\" />\n </div>\n <p className=\"text-sm font-medium text-fg-muted\">{copy.emptyPane}</p>\n <p className=\"max-w-xs text-xs text-fg-subtle\">{copy.emptyPaneDescription}</p>\n </div>\n )\n }\n\n const count = conversation.messages.length\n const latest = conversation.messages[count - 1]\n const header = (\n <div className=\"flex items-start gap-3\">\n {onClose ? (\n <button\n type=\"button\"\n onClick={onClose}\n aria-label={copy.back}\n className=\"-ml-1 rounded-md p-1.5 text-fg-muted hover:bg-surface-hover hover:text-fg lg:hidden\"\n >\n <ChevronLeft size={20} />\n </button>\n ) : (\n <span className=\"flex size-9 shrink-0 items-center justify-center rounded-full bg-primary-subtle\">\n <Mail size={18} className=\"text-primary\" />\n </span>\n )}\n <div className=\"min-w-0 flex-1\">\n <h2 className=\"text-lg font-semibold leading-snug text-fg\">{conversation.subject}</h2>\n <div className=\"mt-1 flex flex-wrap items-center gap-2 text-xs text-fg-muted\">\n <Badge variant=\"secondary\" className=\"font-normal\">\n {count} {count === 1 ? copy.messageNoun : copy.messagesNoun}\n </Badge>\n {latest ? <span suppressHydrationWarning>{fullDate(latest.at)}</span> : null}\n </div>\n </div>\n </div>\n )\n const body = (\n <div className=\"space-y-4\">\n {conversation.messages.map((message) => (\n <MessageCard key={message.id} message={message} copy={copy} />\n ))}\n </div>\n )\n const composer = (\n <ReplyComposer key={threadKey ?? 'thread'} copy={copy} replyLabel={replyLabel} onReply={onReply} />\n )\n\n if (onClose) {\n return (\n <div className=\"flex h-full min-h-0 flex-col\">\n <div className=\"shrink-0 border-b border-border pb-4\">{header}</div>\n <div className=\"app-scroll min-h-0 flex-1 overflow-y-auto py-4\">{body}</div>\n {composer}\n </div>\n )\n }\n return (\n <div className=\"flex h-full min-h-0 flex-col\">\n <div className=\"shrink-0 border-b border-border px-6 py-4\">{header}</div>\n <div className=\"app-scroll min-h-0 flex-1 overflow-y-auto px-6 py-5\">{body}</div>\n {composer}\n </div>\n )\n}\n\nfunction ComposePanel({\n copy,\n variant,\n onCompose,\n onClose,\n}: {\n copy: MailboxInboxCopy\n variant: 'pane' | 'drawer'\n onCompose: (draft: { to: string; subject: string; text: string }) => Promise<void>\n onClose: () => void\n}) {\n const toId = React.useId()\n const subjectId = React.useId()\n const messageId = React.useId()\n const [to, setTo] = React.useState('')\n const [subject, setSubject] = React.useState('')\n const [text, setText] = React.useState('')\n const [busy, setBusy] = React.useState(false)\n const [error, setError] = React.useState<string | null>(null)\n\n const submit = async (event: React.FormEvent<HTMLFormElement>) => {\n event.preventDefault()\n if (busy) return\n setBusy(true)\n setError(null)\n try {\n await onCompose({ to: to.trim(), subject: subject.trim(), text })\n onClose()\n } catch (cause) {\n setError(errorMessage(cause))\n } finally {\n setBusy(false)\n }\n }\n\n const header = (\n <div className=\"flex items-start gap-3\">\n <span className=\"flex size-9 shrink-0 items-center justify-center rounded-full bg-primary-subtle\">\n <SquarePen size={18} className=\"text-primary\" />\n </span>\n <div className=\"min-w-0 flex-1\">\n <h2 className=\"text-lg font-semibold leading-snug text-fg\">{copy.newMessage}</h2>\n </div>\n <button\n type=\"button\"\n onClick={onClose}\n aria-label={copy.cancel}\n className=\"-mr-1 rounded-md p-1.5 text-fg-muted hover:bg-surface-hover hover:text-fg\"\n >\n <X size={18} />\n </button>\n </div>\n )\n const fields = (\n <div className=\"space-y-4\">\n <div className=\"space-y-1.5\">\n <label htmlFor={toId} className=\"block text-xs font-medium text-fg-muted\">\n {copy.to}\n </label>\n <Input\n id={toId}\n type=\"email\"\n required\n value={to}\n onChange={(event) => setTo(event.currentTarget.value)}\n placeholder={copy.toPlaceholder}\n disabled={busy}\n autoComplete=\"email\"\n />\n </div>\n <div className=\"space-y-1.5\">\n <label htmlFor={subjectId} className=\"block text-xs font-medium text-fg-muted\">\n {copy.subject}\n </label>\n <Input\n id={subjectId}\n required\n value={subject}\n onChange={(event) => setSubject(event.currentTarget.value)}\n placeholder={copy.subjectPlaceholder}\n disabled={busy}\n />\n </div>\n <div className=\"space-y-1.5\">\n <label htmlFor={messageId} className=\"block text-xs font-medium text-fg-muted\">\n {copy.message}\n </label>\n <Textarea\n id={messageId}\n required\n value={text}\n onChange={(event) => setText(event.currentTarget.value)}\n placeholder={copy.messagePlaceholder}\n disabled={busy}\n className=\"min-h-40 resize-y\"\n />\n </div>\n </div>\n )\n const actions = (\n <div className=\"flex flex-wrap items-center gap-2\">\n {error ? (\n <div className=\"min-w-0 flex-1\">\n <InlineError message={error} />\n </div>\n ) : (\n <span className=\"flex-1\" />\n )}\n <Button type=\"button\" variant=\"outline\" size=\"sm\" onClick={onClose} disabled={busy}>\n {copy.cancel}\n </Button>\n <Button type=\"submit\" size=\"sm\" disabled={busy}>\n {busy ? <Loader2 size={14} className=\"animate-spin\" /> : <Send size={14} />}\n {busy ? copy.sending : copy.send}\n </Button>\n </div>\n )\n\n if (variant === 'drawer') {\n return (\n <form onSubmit={(event) => void submit(event)} className=\"space-y-4\">\n {fields}\n {actions}\n </form>\n )\n }\n return (\n <form onSubmit={(event) => void submit(event)} className=\"flex h-full min-h-0 flex-col\">\n <div className=\"shrink-0 border-b border-border px-6 py-4\">{header}</div>\n <div className=\"app-scroll min-h-0 flex-1 overflow-y-auto px-6 py-5\">{fields}</div>\n <div className=\"shrink-0 border-t border-border bg-surface px-6 py-4\">{actions}</div>\n </form>\n )\n}\n\nexport type MailboxInboxProps = {\n mailboxes: MailboxOption[]\n activeMailboxId: string\n /**\n * Where switching mailbox goes. Omit it — or pass a single mailbox — on a\n * surface that is about one mailbox, and the switcher is not rendered.\n */\n onSwitchMailbox?: ((id: string) => void) | undefined\n folder: MailFolderKey\n folderCounts?: Partial<Record<MailFolderKey, number>>\n onSwitchFolder: (key: MailFolderKey) => void\n threads: MailThreadListItem[]\n activeThreadId: string | null\n onSelectThread: (id: string | null) => void\n conversation: { subject: string; messages: MailMessageView[] } | null\n onReply: (text: string) => Promise<void>\n onCompose: (draft: { to: string; subject: string; text: string }) => Promise<void>\n onRefresh?: () => void\n /** Whose voice the reply is sent in — e.g. \"Reply as Dana\". */\n replyLabel?: string\n className?: string\n copy?: Partial<MailboxInboxCopy>\n}\n\nexport function MailboxInbox({\n mailboxes,\n activeMailboxId,\n onSwitchMailbox,\n folder,\n folderCounts,\n onSwitchFolder,\n threads,\n activeThreadId,\n onSelectThread,\n conversation,\n onReply,\n onCompose,\n onRefresh,\n replyLabel,\n className,\n copy: copyOverrides,\n}: MailboxInboxProps) {\n const copy = React.useMemo(() => ({ ...DEFAULT_COPY, ...copyOverrides }), [copyOverrides])\n const isDesktop = useIsDesktop()\n const [foldersOpen, setFoldersOpen] = React.useState(false)\n const [composeOpen, setComposeOpen] = React.useState(false)\n\n const total = folderCounts?.[folder] ?? threads.length\n const unread = threads.reduce((count, thread) => (thread.unread ? count + 1 : count), 0)\n\n const openCompose = () => {\n setFoldersOpen(false)\n setComposeOpen(true)\n }\n const closeCompose = () => setComposeOpen(false)\n const selectFolder = (key: MailFolderKey) => {\n setFoldersOpen(false)\n onSwitchFolder(key)\n }\n const selectThread = (id: string) => {\n setComposeOpen(false)\n onSelectThread(id)\n }\n\n const railProps = {\n mailboxes,\n activeMailboxId,\n onSwitchMailbox,\n folder,\n folderCounts,\n onSwitchFolder: selectFolder,\n copy,\n }\n\n const listBody = threads.length ? (\n threads.map((thread) => (\n <ThreadRow\n key={thread.id}\n thread={thread}\n copy={copy}\n selected={thread.id === activeThreadId}\n onOpen={() => selectThread(thread.id)}\n />\n ))\n ) : (\n <div className=\"flex flex-col items-center justify-center px-6 py-20 text-center\">\n <div className=\"flex size-14 items-center justify-center rounded-full bg-bg-subtle text-fg-subtle\">\n <Inbox size={26} />\n </div>\n <p className=\"mt-3 text-sm font-medium text-fg-muted\">{copy.emptyList}</p>\n <p className=\"mt-1 text-xs text-fg-subtle\">{copy.emptyListDescription}</p>\n </div>\n )\n\n return (\n <div className={cn('flex h-full min-h-0 bg-bg-subtle', className)}>\n <FolderRail {...railProps} variant=\"rail\" className=\"hidden w-64 shrink-0 lg:flex\" />\n <section className=\"flex min-w-0 flex-1 flex-col border-r border-border bg-surface lg:w-96 lg:flex-none xl:w-[28rem]\">\n <header className=\"shrink-0 border-b border-border\">\n <div className=\"flex h-14 items-center gap-2 px-3 sm:px-4\">\n <button\n type=\"button\"\n onClick={() => setFoldersOpen(true)}\n aria-label={copy.folders}\n className=\"-ml-1 rounded-md p-1.5 text-fg-muted hover:bg-surface-hover hover:text-fg lg:hidden\"\n >\n <Menu size={20} />\n </button>\n <div className=\"min-w-0 flex-1\">\n <h1 className=\"truncate text-sm font-semibold text-fg\">{copy[folder]}</h1>\n <p className=\"text-[11px] text-fg-subtle\">\n {total} {total === 1 ? copy.conversation : copy.conversations}\n {unread ? ` · ${unread} ${copy.unread}` : ''}\n </p>\n </div>\n {onRefresh ? (\n <button\n type=\"button\"\n onClick={onRefresh}\n title={copy.refresh}\n aria-label={copy.refresh}\n className=\"rounded-md p-1.5 text-fg-muted hover:bg-surface-hover hover:text-fg\"\n >\n <RotateCw size={18} />\n </button>\n ) : null}\n <Button size=\"sm\" onClick={openCompose}>\n <SquarePen size={14} />\n {copy.newMessage}\n </Button>\n </div>\n </header>\n <div className=\"app-scroll min-h-0 flex-1 overflow-y-auto\">{listBody}</div>\n </section>\n <section className=\"hidden min-w-0 flex-1 bg-surface lg:flex\">\n <div className=\"w-full\">\n {composeOpen ? (\n <ComposePanel copy={copy} variant=\"pane\" onCompose={onCompose} onClose={closeCompose} />\n ) : (\n <ConversationPane\n conversation={conversation}\n copy={copy}\n replyLabel={replyLabel}\n threadKey={activeThreadId}\n onReply={onReply}\n />\n )}\n </div>\n </section>\n {!isDesktop ? (\n <>\n <Drawer\n open={foldersOpen}\n onClose={() => setFoldersOpen(false)}\n side=\"left\"\n size=\"sm\"\n title={copy.folders}\n disableFullscreen\n bodyClassName=\"min-h-0 flex-1 overflow-hidden px-4 py-5\"\n >\n <FolderRail {...railProps} variant=\"flyout\" />\n </Drawer>\n <Drawer\n open={Boolean(activeThreadId) && !composeOpen}\n onClose={() => onSelectThread(null)}\n size=\"lg\"\n title={conversation?.subject}\n disableFullscreen\n >\n {activeThreadId && !composeOpen ? (\n <ConversationPane\n conversation={conversation}\n copy={copy}\n replyLabel={replyLabel}\n threadKey={activeThreadId}\n onReply={onReply}\n onClose={() => onSelectThread(null)}\n />\n ) : null}\n </Drawer>\n <Drawer\n open={composeOpen}\n onClose={closeCompose}\n size=\"lg\"\n title={copy.newMessage}\n disableFullscreen\n >\n {composeOpen ? (\n <ComposePanel copy={copy} variant=\"drawer\" onCompose={onCompose} onClose={closeCompose} />\n ) : null}\n </Drawer>\n </>\n ) : null}\n </div>\n )\n}\n\nexport function MailboxInboxSkeleton({ className }: { className?: string } = {}) {\n return (\n <div\n role=\"status\"\n aria-label=\"Loading mailbox\"\n aria-busy=\"true\"\n className={cn('flex h-full min-h-0 bg-bg-subtle', className)}\n >\n <div className=\"hidden w-64 shrink-0 flex-col gap-2 border-r border-border p-3 lg:flex\">\n <Skeleton className=\"h-9 w-full rounded-lg\" />\n <Skeleton className=\"mt-2 h-9 w-full rounded-lg\" />\n {Array.from({ length: 3 }).map((_, index) => (\n <Skeleton key={index} className=\"h-9 w-full rounded-lg\" />\n ))}\n </div>\n <section className=\"flex min-w-0 flex-1 flex-col border-r border-border bg-surface lg:w-96 lg:flex-none xl:w-[28rem]\">\n <div className=\"flex h-14 shrink-0 items-center gap-2 border-b border-border px-3 sm:px-4\">\n <Skeleton className=\"h-5 w-28\" />\n <Skeleton className=\"ml-auto h-8 w-28 rounded-md\" />\n </div>\n <div className=\"flex-1 overflow-hidden\">\n {Array.from({ length: 8 }).map((_, index) => (\n <div\n key={index}\n className=\"flex items-center gap-3 border-b border-border-subtle px-3 py-3 sm:px-4\"\n >\n <Skeleton className=\"size-9 shrink-0 rounded-full\" />\n <div className=\"min-w-0 flex-1 space-y-2\">\n <Skeleton className=\"h-3.5 w-3/4\" />\n <Skeleton className=\"h-3 w-1/2\" />\n </div>\n </div>\n ))}\n </div>\n </section>\n <div className=\"hidden min-w-0 flex-1 items-center justify-center lg:flex\">\n <Skeleton className=\"size-10 rounded-full\" />\n </div>\n </div>\n )\n}\n"]}
|
package/send.d.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { MailboxConnection, SendMailArgs, SendMailResult } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Send one message over the mailbox's SMTP endpoint. Reply threading is the
|
|
4
|
+
* caller's contract: pass the answered Message-ID and the full chain so every
|
|
5
|
+
* client threads the conversation correctly.
|
|
6
|
+
*/
|
|
7
|
+
export declare function sendMail(conn: MailboxConnection, args: SendMailArgs): Promise<SendMailResult>;
|
|
8
|
+
/** Connectivity check for the connect flow: does SMTP accept our login? */
|
|
9
|
+
export declare function verifySmtp(conn: MailboxConnection): Promise<void>;
|
|
10
|
+
//# sourceMappingURL=send.d.ts.map
|
package/send.d.ts.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"send.d.ts","sourceRoot":"","sources":["../src/send.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,iBAAiB,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,SAAS,CAAA;AAmB9E;;;;GAIG;AACH,wBAAsB,QAAQ,CAAC,IAAI,EAAE,iBAAiB,EAAE,IAAI,EAAE,YAAY,GAAG,OAAO,CAAC,cAAc,CAAC,CAiCnG;AAED,2EAA2E;AAC3E,wBAAsB,UAAU,CAAC,IAAI,EAAE,iBAAiB,GAAG,OAAO,CAAC,IAAI,CAAC,CAYvE"}
|
package/send.js
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import nodemailer from 'nodemailer';
|
|
2
|
+
function bracket(id) {
|
|
3
|
+
return id.startsWith('<') ? id : `<${id}>`;
|
|
4
|
+
}
|
|
5
|
+
/**
|
|
6
|
+
* XOAUTH2 when the caller minted an access token, LOGIN/PLAIN otherwise.
|
|
7
|
+
* Nodemailer sends the token as-is — refreshing it is the caller's job, so
|
|
8
|
+
* no clientId/clientSecret ever reaches the transport.
|
|
9
|
+
*/
|
|
10
|
+
function smtpAuth(conn) {
|
|
11
|
+
return conn.accessToken
|
|
12
|
+
? { type: 'OAuth2', user: conn.username, accessToken: conn.accessToken }
|
|
13
|
+
: { user: conn.username, pass: conn.password };
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Send one message over the mailbox's SMTP endpoint. Reply threading is the
|
|
17
|
+
* caller's contract: pass the answered Message-ID and the full chain so every
|
|
18
|
+
* client threads the conversation correctly.
|
|
19
|
+
*/
|
|
20
|
+
export async function sendMail(conn, args) {
|
|
21
|
+
const transport = nodemailer.createTransport({
|
|
22
|
+
host: conn.smtp.host,
|
|
23
|
+
port: conn.smtp.port,
|
|
24
|
+
secure: conn.smtp.secure,
|
|
25
|
+
auth: smtpAuth(conn),
|
|
26
|
+
});
|
|
27
|
+
try {
|
|
28
|
+
const info = await transport.sendMail({
|
|
29
|
+
from: args.fromName ? { name: args.fromName, address: conn.address } : conn.address,
|
|
30
|
+
to: args.to.map((a) => (a.name ? { name: a.name, address: a.address } : a.address)),
|
|
31
|
+
cc: args.cc?.map((a) => (a.name ? { name: a.name, address: a.address } : a.address)),
|
|
32
|
+
subject: args.subject,
|
|
33
|
+
text: args.text,
|
|
34
|
+
html: args.html,
|
|
35
|
+
inReplyTo: args.inReplyTo ? bracket(args.inReplyTo) : undefined,
|
|
36
|
+
references: args.references?.length ? args.references.map(bracket).join(' ') : undefined,
|
|
37
|
+
attachments: args.attachments?.length
|
|
38
|
+
? args.attachments.map((a) => ({
|
|
39
|
+
filename: a.filename,
|
|
40
|
+
content: Buffer.isBuffer(a.content) ? a.content : Buffer.from(a.content),
|
|
41
|
+
contentType: a.contentType,
|
|
42
|
+
cid: a.contentId,
|
|
43
|
+
}))
|
|
44
|
+
: undefined,
|
|
45
|
+
});
|
|
46
|
+
return {
|
|
47
|
+
messageId: info.messageId.replace(/^<|>$/g, ''),
|
|
48
|
+
sentAt: new Date(),
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
finally {
|
|
52
|
+
transport.close();
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
/** Connectivity check for the connect flow: does SMTP accept our login? */
|
|
56
|
+
export async function verifySmtp(conn) {
|
|
57
|
+
const transport = nodemailer.createTransport({
|
|
58
|
+
host: conn.smtp.host,
|
|
59
|
+
port: conn.smtp.port,
|
|
60
|
+
secure: conn.smtp.secure,
|
|
61
|
+
auth: smtpAuth(conn),
|
|
62
|
+
});
|
|
63
|
+
try {
|
|
64
|
+
await transport.verify();
|
|
65
|
+
}
|
|
66
|
+
finally {
|
|
67
|
+
transport.close();
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
//# sourceMappingURL=send.js.map
|
package/send.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"send.js","sourceRoot":"","sources":["../src/send.ts"],"names":[],"mappings":"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","sourcesContent":["import nodemailer from 'nodemailer'\nimport type { MailboxConnection, SendMailArgs, SendMailResult } from './types'\n\nfunction bracket(id: string): string {\n return id.startsWith('<') ? id : `<${id}>`\n}\n\n/**\n * XOAUTH2 when the caller minted an access token, LOGIN/PLAIN otherwise.\n * Nodemailer sends the token as-is — refreshing it is the caller's job, so\n * no clientId/clientSecret ever reaches the transport.\n */\nfunction smtpAuth(\n conn: MailboxConnection,\n): { user: string; pass: string } | { type: 'OAuth2'; user: string; accessToken: string } {\n return conn.accessToken\n ? { type: 'OAuth2', user: conn.username, accessToken: conn.accessToken }\n : { user: conn.username, pass: conn.password }\n}\n\n/**\n * Send one message over the mailbox's SMTP endpoint. Reply threading is the\n * caller's contract: pass the answered Message-ID and the full chain so every\n * client threads the conversation correctly.\n */\nexport async function sendMail(conn: MailboxConnection, args: SendMailArgs): Promise<SendMailResult> {\n const transport = nodemailer.createTransport({\n host: conn.smtp.host,\n port: conn.smtp.port,\n secure: conn.smtp.secure,\n auth: smtpAuth(conn),\n })\n try {\n const info = await transport.sendMail({\n from: args.fromName ? { name: args.fromName, address: conn.address } : conn.address,\n to: args.to.map((a) => (a.name ? { name: a.name, address: a.address } : a.address)),\n cc: args.cc?.map((a) => (a.name ? { name: a.name, address: a.address } : a.address)),\n subject: args.subject,\n text: args.text,\n html: args.html,\n inReplyTo: args.inReplyTo ? bracket(args.inReplyTo) : undefined,\n references: args.references?.length ? args.references.map(bracket).join(' ') : undefined,\n attachments: args.attachments?.length\n ? args.attachments.map((a) => ({\n filename: a.filename,\n content: Buffer.isBuffer(a.content) ? a.content : Buffer.from(a.content),\n contentType: a.contentType,\n cid: a.contentId,\n }))\n : undefined,\n })\n return {\n messageId: info.messageId.replace(/^<|>$/g, ''),\n sentAt: new Date(),\n }\n } finally {\n transport.close()\n }\n}\n\n/** Connectivity check for the connect flow: does SMTP accept our login? */\nexport async function verifySmtp(conn: MailboxConnection): Promise<void> {\n const transport = nodemailer.createTransport({\n host: conn.smtp.host,\n port: conn.smtp.port,\n secure: conn.smtp.secure,\n auth: smtpAuth(conn),\n })\n try {\n await transport.verify()\n } finally {\n transport.close()\n }\n}\n"]}
|
package/sync.d.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { MailboxConnection, MailboxStore, SyncResult } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* One incremental IMAP sync pass over INBOX. UIDVALIDITY change resets the
|
|
4
|
+
* cursor (server renumbered the mailbox); otherwise only UIDs above the last
|
|
5
|
+
* seen are fetched. Dedupe is by Message-ID via the store, so overlapping
|
|
6
|
+
* passes are idempotent.
|
|
7
|
+
*/
|
|
8
|
+
export declare function syncMailbox(conn: MailboxConnection, store: MailboxStore): Promise<SyncResult>;
|
|
9
|
+
/** Cheap connectivity check used by the connect flow: can we authenticate? */
|
|
10
|
+
export declare function verifyImap(conn: MailboxConnection): Promise<void>;
|
|
11
|
+
//# sourceMappingURL=sync.d.ts.map
|
package/sync.d.ts.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sync.d.ts","sourceRoot":"","sources":["../src/sync.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAA2C,iBAAiB,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,SAAS,CAAA;AA2BnH;;;;;GAKG;AACH,wBAAsB,WAAW,CAAC,IAAI,EAAE,iBAAiB,EAAE,KAAK,EAAE,YAAY,GAAG,OAAO,CAAC,UAAU,CAAC,CAkEnG;AAED,8EAA8E;AAC9E,wBAAsB,UAAU,CAAC,IAAI,EAAE,iBAAiB,GAAG,OAAO,CAAC,IAAI,CAAC,CAUvE"}
|
package/sync.js
ADDED
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import { ImapFlow } from 'imapflow';
|
|
2
|
+
import { simpleParser } from 'mailparser';
|
|
3
|
+
/**
|
|
4
|
+
* XOAUTH2 when the caller minted an access token, LOGIN/PLAIN otherwise.
|
|
5
|
+
* imapflow negotiates the mechanism from the shape of this object.
|
|
6
|
+
*/
|
|
7
|
+
function imapAuth(conn) {
|
|
8
|
+
return conn.accessToken
|
|
9
|
+
? { user: conn.username, accessToken: conn.accessToken }
|
|
10
|
+
: { user: conn.username, pass: conn.password };
|
|
11
|
+
}
|
|
12
|
+
function stripAngles(id) {
|
|
13
|
+
if (!id)
|
|
14
|
+
return null;
|
|
15
|
+
const trimmed = id.trim();
|
|
16
|
+
return trimmed.replace(/^<|>$/g, '') || null;
|
|
17
|
+
}
|
|
18
|
+
function toAddresses(value) {
|
|
19
|
+
const objects = Array.isArray(value) ? value : value ? [value] : [];
|
|
20
|
+
return objects.flatMap((o) => o.value
|
|
21
|
+
.filter((v) => v.address)
|
|
22
|
+
.map((v) => ({ address: v.address, ...(v.name ? { name: v.name } : {}) })));
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* One incremental IMAP sync pass over INBOX. UIDVALIDITY change resets the
|
|
26
|
+
* cursor (server renumbered the mailbox); otherwise only UIDs above the last
|
|
27
|
+
* seen are fetched. Dedupe is by Message-ID via the store, so overlapping
|
|
28
|
+
* passes are idempotent.
|
|
29
|
+
*/
|
|
30
|
+
export async function syncMailbox(conn, store) {
|
|
31
|
+
const client = new ImapFlow({
|
|
32
|
+
host: conn.imap.host,
|
|
33
|
+
port: conn.imap.port,
|
|
34
|
+
secure: conn.imap.secure,
|
|
35
|
+
auth: imapAuth(conn),
|
|
36
|
+
logger: false,
|
|
37
|
+
});
|
|
38
|
+
await client.connect();
|
|
39
|
+
try {
|
|
40
|
+
const lock = await client.getMailboxLock('INBOX');
|
|
41
|
+
try {
|
|
42
|
+
const mailbox = client.mailbox;
|
|
43
|
+
if (!mailbox || typeof mailbox === 'boolean')
|
|
44
|
+
throw new Error('INBOX could not be opened');
|
|
45
|
+
const uidValidity = String(mailbox.uidValidity ?? '0');
|
|
46
|
+
const prior = await store.getCursor();
|
|
47
|
+
const fromUid = prior && prior.uidValidity === uidValidity ? prior.lastUid + 1 : 1;
|
|
48
|
+
let fetched = 0;
|
|
49
|
+
let saved = 0;
|
|
50
|
+
let lastUid = prior && prior.uidValidity === uidValidity ? prior.lastUid : 0;
|
|
51
|
+
for await (const item of client.fetch(`${fromUid}:*`, { uid: true, source: true }, { uid: true })) {
|
|
52
|
+
if (item.uid < fromUid)
|
|
53
|
+
continue;
|
|
54
|
+
fetched += 1;
|
|
55
|
+
lastUid = Math.max(lastUid, item.uid);
|
|
56
|
+
if (!item.source)
|
|
57
|
+
continue;
|
|
58
|
+
const parsed = await simpleParser(item.source);
|
|
59
|
+
const messageId = stripAngles(parsed.messageId) ?? `uid-${uidValidity}-${item.uid}@${conn.imap.host}`;
|
|
60
|
+
if (await store.hasMessage(messageId))
|
|
61
|
+
continue;
|
|
62
|
+
const references = (Array.isArray(parsed.references) ? parsed.references : parsed.references ? [parsed.references] : [])
|
|
63
|
+
.map((r) => stripAngles(r))
|
|
64
|
+
.filter((r) => r !== null);
|
|
65
|
+
const from = toAddresses(parsed.from)[0] ?? { address: 'unknown@invalid' };
|
|
66
|
+
const message = {
|
|
67
|
+
messageId,
|
|
68
|
+
threadKey: references[0] ?? messageId,
|
|
69
|
+
inReplyTo: stripAngles(parsed.inReplyTo),
|
|
70
|
+
from,
|
|
71
|
+
to: toAddresses(parsed.to),
|
|
72
|
+
cc: toAddresses(parsed.cc),
|
|
73
|
+
subject: parsed.subject ?? '(no subject)',
|
|
74
|
+
text: parsed.text ?? '',
|
|
75
|
+
html: typeof parsed.html === 'string' ? parsed.html : null,
|
|
76
|
+
sentAt: parsed.date ?? new Date(),
|
|
77
|
+
uid: item.uid,
|
|
78
|
+
attachments: parsed.attachments.map((a) => ({
|
|
79
|
+
filename: a.filename ?? 'attachment',
|
|
80
|
+
contentType: a.contentType,
|
|
81
|
+
size: a.size,
|
|
82
|
+
content: a.content,
|
|
83
|
+
})),
|
|
84
|
+
};
|
|
85
|
+
await store.saveInbound(message);
|
|
86
|
+
saved += 1;
|
|
87
|
+
}
|
|
88
|
+
const cursor = { uidValidity, lastUid };
|
|
89
|
+
await store.saveCursor(cursor);
|
|
90
|
+
return { fetched, saved, cursor };
|
|
91
|
+
}
|
|
92
|
+
finally {
|
|
93
|
+
lock.release();
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
finally {
|
|
97
|
+
await client.logout().catch(() => client.close());
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
/** Cheap connectivity check used by the connect flow: can we authenticate? */
|
|
101
|
+
export async function verifyImap(conn) {
|
|
102
|
+
const client = new ImapFlow({
|
|
103
|
+
host: conn.imap.host,
|
|
104
|
+
port: conn.imap.port,
|
|
105
|
+
secure: conn.imap.secure,
|
|
106
|
+
auth: imapAuth(conn),
|
|
107
|
+
logger: false,
|
|
108
|
+
});
|
|
109
|
+
await client.connect();
|
|
110
|
+
await client.logout().catch(() => client.close());
|
|
111
|
+
}
|
|
112
|
+
//# sourceMappingURL=sync.js.map
|
package/sync.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sync.js","sourceRoot":"","sources":["../src/sync.ts"],"names":[],"mappings":"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","sourcesContent":["import { ImapFlow } from 'imapflow'\nimport { simpleParser, type AddressObject } from 'mailparser'\nimport type { ImapCursor, InboundMessage, MailAddress, MailboxConnection, MailboxStore, SyncResult } from './types'\n\n/**\n * XOAUTH2 when the caller minted an access token, LOGIN/PLAIN otherwise.\n * imapflow negotiates the mechanism from the shape of this object.\n */\nfunction imapAuth(conn: MailboxConnection): { user: string; pass: string } | { user: string; accessToken: string } {\n return conn.accessToken\n ? { user: conn.username, accessToken: conn.accessToken }\n : { user: conn.username, pass: conn.password }\n}\n\nfunction stripAngles(id: string | undefined | null): string | null {\n if (!id) return null\n const trimmed = id.trim()\n return trimmed.replace(/^<|>$/g, '') || null\n}\n\nfunction toAddresses(value: AddressObject | AddressObject[] | undefined): MailAddress[] {\n const objects = Array.isArray(value) ? value : value ? [value] : []\n return objects.flatMap((o) =>\n o.value\n .filter((v) => v.address)\n .map((v) => ({ address: v.address!, ...(v.name ? { name: v.name } : {}) })),\n )\n}\n\n/**\n * One incremental IMAP sync pass over INBOX. UIDVALIDITY change resets the\n * cursor (server renumbered the mailbox); otherwise only UIDs above the last\n * seen are fetched. Dedupe is by Message-ID via the store, so overlapping\n * passes are idempotent.\n */\nexport async function syncMailbox(conn: MailboxConnection, store: MailboxStore): Promise<SyncResult> {\n const client = new ImapFlow({\n host: conn.imap.host,\n port: conn.imap.port,\n secure: conn.imap.secure,\n auth: imapAuth(conn),\n logger: false,\n })\n await client.connect()\n try {\n const lock = await client.getMailboxLock('INBOX')\n try {\n const mailbox = client.mailbox\n if (!mailbox || typeof mailbox === 'boolean') throw new Error('INBOX could not be opened')\n const uidValidity = String(mailbox.uidValidity ?? '0')\n const prior = await store.getCursor()\n const fromUid = prior && prior.uidValidity === uidValidity ? prior.lastUid + 1 : 1\n\n let fetched = 0\n let saved = 0\n let lastUid = prior && prior.uidValidity === uidValidity ? prior.lastUid : 0\n\n for await (const item of client.fetch(`${fromUid}:*`, { uid: true, source: true }, { uid: true })) {\n if (item.uid < fromUid) continue\n fetched += 1\n lastUid = Math.max(lastUid, item.uid)\n if (!item.source) continue\n const parsed = await simpleParser(item.source)\n const messageId = stripAngles(parsed.messageId) ?? `uid-${uidValidity}-${item.uid}@${conn.imap.host}`\n if (await store.hasMessage(messageId)) continue\n const references = (Array.isArray(parsed.references) ? parsed.references : parsed.references ? [parsed.references] : [])\n .map((r) => stripAngles(r))\n .filter((r): r is string => r !== null)\n const from = toAddresses(parsed.from)[0] ?? { address: 'unknown@invalid' }\n const message: InboundMessage = {\n messageId,\n threadKey: references[0] ?? messageId,\n inReplyTo: stripAngles(parsed.inReplyTo),\n from,\n to: toAddresses(parsed.to),\n cc: toAddresses(parsed.cc),\n subject: parsed.subject ?? '(no subject)',\n text: parsed.text ?? '',\n html: typeof parsed.html === 'string' ? parsed.html : null,\n sentAt: parsed.date ?? new Date(),\n uid: item.uid,\n attachments: parsed.attachments.map((a) => ({\n filename: a.filename ?? 'attachment',\n contentType: a.contentType,\n size: a.size,\n content: a.content,\n })),\n }\n await store.saveInbound(message)\n saved += 1\n }\n\n const cursor: ImapCursor = { uidValidity, lastUid }\n await store.saveCursor(cursor)\n return { fetched, saved, cursor }\n } finally {\n lock.release()\n }\n } finally {\n await client.logout().catch(() => client.close())\n }\n}\n\n/** Cheap connectivity check used by the connect flow: can we authenticate? */\nexport async function verifyImap(conn: MailboxConnection): Promise<void> {\n const client = new ImapFlow({\n host: conn.imap.host,\n port: conn.imap.port,\n secure: conn.imap.secure,\n auth: imapAuth(conn),\n logger: false,\n })\n await client.connect()\n await client.logout().catch(() => client.close())\n}\n"]}
|
package/types.d.ts
ADDED
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The mail engine is persistence-injected: it speaks IMAP/SMTP and RFC 5322,
|
|
3
|
+
* the application owns the ledger. Config carries the unsealed credential for
|
|
4
|
+
* the duration of one operation only — never store it.
|
|
5
|
+
*
|
|
6
|
+
* Two credential shapes, one connection type. A password authenticates with
|
|
7
|
+
* LOGIN/PLAIN; an `accessToken` authenticates with XOAUTH2, which is what
|
|
8
|
+
* Google Workspace and Microsoft 365 require (Microsoft has retired basic
|
|
9
|
+
* auth for IMAP/SMTP entirely). Access tokens are minted per operation from a
|
|
10
|
+
* refresh token the application holds — this type never sees the long-lived
|
|
11
|
+
* credential.
|
|
12
|
+
*/
|
|
13
|
+
export type MailboxConnection = {
|
|
14
|
+
address: string;
|
|
15
|
+
imap: {
|
|
16
|
+
host: string;
|
|
17
|
+
port: number;
|
|
18
|
+
secure: boolean;
|
|
19
|
+
};
|
|
20
|
+
smtp: {
|
|
21
|
+
host: string;
|
|
22
|
+
port: number;
|
|
23
|
+
secure: boolean;
|
|
24
|
+
};
|
|
25
|
+
username: string;
|
|
26
|
+
/** LOGIN/PLAIN password. Ignored (and may be '') when `accessToken` is set. */
|
|
27
|
+
password: string;
|
|
28
|
+
/** Short-lived OAuth 2.0 access token. When set, both endpoints use XOAUTH2. */
|
|
29
|
+
accessToken?: string;
|
|
30
|
+
};
|
|
31
|
+
export type MailAddress = {
|
|
32
|
+
name?: string;
|
|
33
|
+
address: string;
|
|
34
|
+
};
|
|
35
|
+
export type InboundMessage = {
|
|
36
|
+
/** RFC 5322 Message-ID (angle brackets stripped). */
|
|
37
|
+
messageId: string;
|
|
38
|
+
/** Thread key: the root of the References chain, else this message's id. */
|
|
39
|
+
threadKey: string;
|
|
40
|
+
inReplyTo: string | null;
|
|
41
|
+
from: MailAddress;
|
|
42
|
+
to: MailAddress[];
|
|
43
|
+
cc: MailAddress[];
|
|
44
|
+
subject: string;
|
|
45
|
+
text: string;
|
|
46
|
+
html: string | null;
|
|
47
|
+
sentAt: Date;
|
|
48
|
+
/** IMAP UID, for cursor advancement. */
|
|
49
|
+
uid: number;
|
|
50
|
+
attachments: {
|
|
51
|
+
filename: string;
|
|
52
|
+
contentType: string;
|
|
53
|
+
size: number;
|
|
54
|
+
content: Buffer;
|
|
55
|
+
}[];
|
|
56
|
+
};
|
|
57
|
+
/** Incremental sync position for one IMAP mailbox. */
|
|
58
|
+
export type ImapCursor = {
|
|
59
|
+
uidValidity: string;
|
|
60
|
+
lastUid: number;
|
|
61
|
+
};
|
|
62
|
+
export type MailboxStore = {
|
|
63
|
+
getCursor: () => Promise<ImapCursor | null>;
|
|
64
|
+
saveCursor: (cursor: ImapCursor) => Promise<void>;
|
|
65
|
+
/** Return true if the message id is already in the ledger (dedupe). */
|
|
66
|
+
hasMessage: (messageId: string) => Promise<boolean>;
|
|
67
|
+
/** Persist one inbound message (thread upsert included application-side). */
|
|
68
|
+
saveInbound: (message: InboundMessage) => Promise<void>;
|
|
69
|
+
};
|
|
70
|
+
export type OutboundAttachment = {
|
|
71
|
+
filename: string;
|
|
72
|
+
content: Buffer | Uint8Array;
|
|
73
|
+
contentType?: string;
|
|
74
|
+
/** Content-ID for inline references from the HTML body (`cid:` URLs). */
|
|
75
|
+
contentId?: string;
|
|
76
|
+
};
|
|
77
|
+
export type SendMailArgs = {
|
|
78
|
+
to: MailAddress[];
|
|
79
|
+
cc?: MailAddress[];
|
|
80
|
+
subject: string;
|
|
81
|
+
text: string;
|
|
82
|
+
html?: string;
|
|
83
|
+
/** Reply threading: the Message-ID being answered and the full chain. */
|
|
84
|
+
inReplyTo?: string;
|
|
85
|
+
references?: string[];
|
|
86
|
+
fromName?: string;
|
|
87
|
+
attachments?: OutboundAttachment[];
|
|
88
|
+
};
|
|
89
|
+
export type SendMailResult = {
|
|
90
|
+
messageId: string;
|
|
91
|
+
sentAt: Date;
|
|
92
|
+
};
|
|
93
|
+
export type SyncResult = {
|
|
94
|
+
fetched: number;
|
|
95
|
+
saved: number;
|
|
96
|
+
cursor: ImapCursor;
|
|
97
|
+
};
|
|
98
|
+
//# sourceMappingURL=types.d.ts.map
|
package/types.d.ts.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
package/types.js
ADDED
package/types.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"","sourcesContent":["/**\n * The mail engine is persistence-injected: it speaks IMAP/SMTP and RFC 5322,\n * the application owns the ledger. Config carries the unsealed credential for\n * the duration of one operation only — never store it.\n *\n * Two credential shapes, one connection type. A password authenticates with\n * LOGIN/PLAIN; an `accessToken` authenticates with XOAUTH2, which is what\n * Google Workspace and Microsoft 365 require (Microsoft has retired basic\n * auth for IMAP/SMTP entirely). Access tokens are minted per operation from a\n * refresh token the application holds — this type never sees the long-lived\n * credential.\n */\nexport type MailboxConnection = {\n address: string\n imap: { host: string; port: number; secure: boolean }\n smtp: { host: string; port: number; secure: boolean }\n username: string\n /** LOGIN/PLAIN password. Ignored (and may be '') when `accessToken` is set. */\n password: string\n /** Short-lived OAuth 2.0 access token. When set, both endpoints use XOAUTH2. */\n accessToken?: string\n}\n\nexport type MailAddress = { name?: string; address: string }\n\nexport type InboundMessage = {\n /** RFC 5322 Message-ID (angle brackets stripped). */\n messageId: string\n /** Thread key: the root of the References chain, else this message's id. */\n threadKey: string\n inReplyTo: string | null\n from: MailAddress\n to: MailAddress[]\n cc: MailAddress[]\n subject: string\n text: string\n html: string | null\n sentAt: Date\n /** IMAP UID, for cursor advancement. */\n uid: number\n attachments: { filename: string; contentType: string; size: number; content: Buffer }[]\n}\n\n/** Incremental sync position for one IMAP mailbox. */\nexport type ImapCursor = {\n uidValidity: string\n lastUid: number\n}\n\nexport type MailboxStore = {\n getCursor: () => Promise<ImapCursor | null>\n saveCursor: (cursor: ImapCursor) => Promise<void>\n /** Return true if the message id is already in the ledger (dedupe). */\n hasMessage: (messageId: string) => Promise<boolean>\n /** Persist one inbound message (thread upsert included application-side). */\n saveInbound: (message: InboundMessage) => Promise<void>\n}\n\nexport type OutboundAttachment = {\n filename: string\n content: Buffer | Uint8Array\n contentType?: string\n /** Content-ID for inline references from the HTML body (`cid:` URLs). */\n contentId?: string\n}\n\nexport type SendMailArgs = {\n to: MailAddress[]\n cc?: MailAddress[]\n subject: string\n text: string\n html?: string\n /** Reply threading: the Message-ID being answered and the full chain. */\n inReplyTo?: string\n references?: string[]\n fromName?: string\n attachments?: OutboundAttachment[]\n}\n\nexport type SendMailResult = {\n messageId: string\n sentAt: Date\n}\n\nexport type SyncResult = {\n fetched: number\n saved: number\n cursor: ImapCursor\n}\n"]}
|