{"_id":"@cool-ai/beach-transport-email","_rev":"5-80c753127f64fba345fb6d678c8fdd5f","name":"@cool-ai/beach-transport-email","dist-tags":{"latest":"0.3.1"},"versions":{"0.1.0":{"name":"@cool-ai/beach-transport-email","version":"0.1.0","license":"Apache-2.0","_id":"@cool-ai/beach-transport-email@0.1.0","maintainers":[{"name":"johncandrew","email":"johncandrew@yahoo.co.uk"}],"homepage":"https://cool-ai.org","bugs":{"url":"https://gitlab.com/johncandrew/beach/-/issues"},"dist":{"shasum":"a998afede7dadd0184625f32cf9fcb7e32afb1b7","tarball":"https://registry.npmjs.org/@cool-ai/beach-transport-email/-/beach-transport-email-0.1.0.tgz","fileCount":23,"integrity":"sha512-n359wkc/lgrmaZvKOpgn8R6PP6nqh4OpWZYESpi+h7KbVcKwRWCIaFnPpi9jDqG6X9RIr3BtgxR52T9NRqm0Sw==","signatures":[{"sig":"MEUCIQClS6XtcST6BOWEIoiyZEcjngLNdTI/RaS11Hz4TbUpWQIgd8EsApmJ6GURw3zfeoNRYqkKoIEBAQgFg40ZXtLhbvE=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":53734},"type":"module","_from":"file:cool-ai-beach-transport-email-0.1.0.tgz","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"}},"scripts":{"dev":"tsc --project tsconfig.json --watch","test":"vitest run","build":"tsc --project tsconfig.json","typecheck":"tsc --noEmit","test:watch":"vitest"},"_npmUser":{"name":"johncandrew","email":"johncandrew@yahoo.co.uk"},"_resolved":"/tmp/02536bd9923bb266919c7690bf2b73c6/cool-ai-beach-transport-email-0.1.0.tgz","_integrity":"sha512-n359wkc/lgrmaZvKOpgn8R6PP6nqh4OpWZYESpi+h7KbVcKwRWCIaFnPpi9jDqG6X9RIr3BtgxR52T9NRqm0Sw==","repository":{"url":"git+https://gitlab.com/johncandrew/beach.git","type":"git","directory":"packages/transport-email"},"_npmVersion":"10.9.7","description":"IMAP and SMTP transport adapters for Beach — the wire layer underneath @cool-ai/beach-channel-email.","directories":{},"_nodeVersion":"22.22.2","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"vitest":"^2.0.0","imapflow":"^1.3.1","mailparser":"^3.9.8","nodemailer":"^8.0.5","typescript":"^5.5.0","@types/mailparser":"^3.4.0","@types/nodemailer":"^6.4.0"},"peerDependencies":{"imapflow":"^1.3.1","mailparser":"^3.9.8","nodemailer":"^8.0.5"},"_npmOperationalInternal":{"tmp":"tmp/beach-transport-email_0.1.0_1777415996913_0.10741170298102909","host":"s3://npm-registry-packages-npm-production"},"deprecated":"No longer published. Folded into @cool-ai/beach-channel-email."},"0.2.0":{"name":"@cool-ai/beach-transport-email","version":"0.2.0","license":"Apache-2.0","_id":"@cool-ai/beach-transport-email@0.2.0","maintainers":[{"name":"johncandrew","email":"johncandrew@yahoo.co.uk"}],"homepage":"https://cool-ai.org","bugs":{"url":"https://gitlab.com/johncandrew/beach/-/issues"},"dist":{"shasum":"43312b217e6ce1663a03ac7726f44bdff03e25c0","tarball":"https://registry.npmjs.org/@cool-ai/beach-transport-email/-/beach-transport-email-0.2.0.tgz","fileCount":23,"integrity":"sha512-5PgpVt/72Cvu5mlTG43Dy6gcP6CQ86Tkd2oism5mG0EcNjVTr1tBGqZ3yNCUMOiArTsS2Tr2f0QM5bk/8Eg0vg==","signatures":[{"sig":"MEYCIQCpYgdXefCd38KHWC2SjS2AwpczDVxohUDRnH7DJmlbCwIhAO9ecbYKByIQ3jB/9FBMA6TiivaboUn0t/oyTaA3mxee","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":58969},"type":"module","_from":"file:cool-ai-beach-transport-email-0.2.0.tgz","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"}},"scripts":{"dev":"tsc --project tsconfig.json --watch","test":"vitest run","build":"tsc --project tsconfig.json","typecheck":"tsc --noEmit","test:watch":"vitest"},"_npmUser":{"name":"johncandrew","email":"johncandrew@yahoo.co.uk"},"_resolved":"/tmp/8128acb98ac16b6ab6e2f50c7cc8d005/cool-ai-beach-transport-email-0.2.0.tgz","_integrity":"sha512-5PgpVt/72Cvu5mlTG43Dy6gcP6CQ86Tkd2oism5mG0EcNjVTr1tBGqZ3yNCUMOiArTsS2Tr2f0QM5bk/8Eg0vg==","repository":{"url":"git+https://gitlab.com/johncandrew/beach.git","type":"git","directory":"packages/transport-email"},"_npmVersion":"10.9.7","description":"IMAP and SMTP transport adapters for Beach — the wire layer underneath @cool-ai/beach-channel-email.","directories":{},"_nodeVersion":"22.22.2","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"vitest":"^2.0.0","imapflow":"^1.3.1","mailparser":"^3.9.8","nodemailer":"^8.0.5","typescript":"^5.5.0","@types/mailparser":"^3.4.0","@types/nodemailer":"^6.4.0"},"peerDependencies":{"imapflow":"^1.3.1","mailparser":"^3.9.8","nodemailer":"^8.0.5"},"_npmOperationalInternal":{"tmp":"tmp/beach-transport-email_0.2.0_1777476724177_0.6858210546032217","host":"s3://npm-registry-packages-npm-production"},"deprecated":"No longer published. Folded into @cool-ai/beach-channel-email."},"0.3.0":{"name":"@cool-ai/beach-transport-email","version":"0.3.0","license":"Apache-2.0","_id":"@cool-ai/beach-transport-email@0.3.0","maintainers":[{"name":"johncandrew","email":"johncandrew@yahoo.co.uk"}],"homepage":"https://cool-ai.org","bugs":{"url":"https://gitlab.com/johncandrew/beach/-/issues"},"dist":{"shasum":"fcc25b08306ca2472feeb31b46d597e72d154835","tarball":"https://registry.npmjs.org/@cool-ai/beach-transport-email/-/beach-transport-email-0.3.0.tgz","fileCount":27,"integrity":"sha512-kHjrMTQ6EyOtgR0ynnXNvAdUo/Jy69SHhJk0CfB2PIudDiiA7xVOBcia5P84bnk9rO44Ufr4qE9SAfxVxzDwtw==","signatures":[{"sig":"MEUCIQCi6T8+e+f4wgHLztjLHLTs7+AOGKQlTCKC2f6QV1EmpQIgApQJGXftny9hDAAftwWlf5Aon+ZtcNdhmoo58S2Trgw=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":93506},"type":"module","_from":"file:cool-ai-beach-transport-email-0.3.0.tgz","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"}},"scripts":{"dev":"tsc --project tsconfig.json --watch","test":"vitest run","build":"tsc --project tsconfig.json","typecheck":"tsc --noEmit","test:watch":"vitest"},"_npmUser":{"name":"johncandrew","email":"johncandrew@yahoo.co.uk"},"_resolved":"/tmp/6b552b6df5d000ef679b498bdb14eff4/cool-ai-beach-transport-email-0.3.0.tgz","_integrity":"sha512-kHjrMTQ6EyOtgR0ynnXNvAdUo/Jy69SHhJk0CfB2PIudDiiA7xVOBcia5P84bnk9rO44Ufr4qE9SAfxVxzDwtw==","repository":{"url":"git+https://gitlab.com/johncandrew/beach.git","type":"git","directory":"packages/transport-email"},"_npmVersion":"10.9.7","description":"IMAP and SMTP transport adapters for Beach — the wire layer underneath @cool-ai/beach-channel-email.","directories":{},"_nodeVersion":"22.22.2","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"vitest":"^2.0.0","imapflow":"^1.3.1","mailparser":"^3.9.8","nodemailer":"^8.0.5","typescript":"^5.5.0","@types/mailparser":"^3.4.0","@types/nodemailer":"^6.4.0"},"peerDependencies":{"imapflow":"^1.3.1","mailparser":"^3.9.8","nodemailer":"^8.0.5"},"_npmOperationalInternal":{"tmp":"tmp/beach-transport-email_0.3.0_1778003256998_0.037666326503781544","host":"s3://npm-registry-packages-npm-production"},"deprecated":"No longer published. Folded into @cool-ai/beach-channel-email."},"0.3.1":{"name":"@cool-ai/beach-transport-email","version":"0.3.1","license":"Apache-2.0","_id":"@cool-ai/beach-transport-email@0.3.1","maintainers":[{"name":"johncandrew","email":"johncandrew@yahoo.co.uk"}],"homepage":"https://cool-ai.org","bugs":{"url":"https://gitlab.com/johncandrew/beach/-/issues"},"dist":{"shasum":"c244c55423606e85fcc519e39c357fabf7b48c84","tarball":"https://registry.npmjs.org/@cool-ai/beach-transport-email/-/beach-transport-email-0.3.1.tgz","fileCount":19,"integrity":"sha512-kQ0a8MbUlLT6uNSxCZehqfh919dNNzZTGtX3XyeRPqECDpWtPB5nTktpMteo/WtBsiVjHWT/13W7wodO8NWvgA==","signatures":[{"sig":"MEUCIQCoXAUj10l4EEZxpvh5YiVdPOn1p9n9TsGntTyouk7ILgIgZvucA2S52+Yf1j6gH4SAfmisyih1BHBTXewR6kGCpXg=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":62876},"type":"module","_from":"file:cool-ai-beach-transport-email-0.3.1.tgz","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"}},"scripts":{"dev":"tsc --project tsconfig.json --watch","test":"vitest run","build":"tsc --project tsconfig.json","coverage":"vitest run --coverage","typecheck":"tsc --noEmit","test:watch":"vitest"},"_npmUser":{"name":"johncandrew","email":"johncandrew@yahoo.co.uk"},"_resolved":"/tmp/812ca9bc4d1af5c3708f695ed8c65592/cool-ai-beach-transport-email-0.3.1.tgz","_integrity":"sha512-kQ0a8MbUlLT6uNSxCZehqfh919dNNzZTGtX3XyeRPqECDpWtPB5nTktpMteo/WtBsiVjHWT/13W7wodO8NWvgA==","repository":{"url":"git+https://gitlab.com/johncandrew/beach.git","type":"git","directory":"packages/transport-email"},"_npmVersion":"10.9.7","description":"IMAP and SMTP transport adapters for Beach — the wire layer underneath @cool-ai/beach-channel-email.","directories":{},"_nodeVersion":"22.22.2","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"vitest":"^2.0.0","imapflow":"^1.3.1","mailparser":"^3.9.8","nodemailer":"^8.0.5","typescript":"^5.5.0","@types/mailparser":"^3.4.0","@types/nodemailer":"^6.4.0"},"peerDependencies":{"imapflow":"^1.3.1","mailparser":"^3.9.8","nodemailer":"^8.0.5"},"_npmOperationalInternal":{"tmp":"tmp/beach-transport-email_0.3.1_1778466314785_0.7095369753075302","host":"s3://npm-registry-packages-npm-production"},"deprecated":"No longer published. Folded into @cool-ai/beach-channel-email."}},"time":{"created":"2026-04-28T22:39:56.835Z","modified":"2026-06-06T07:57:19.064Z","0.1.0":"2026-04-28T22:39:57.059Z","0.2.0":"2026-04-29T15:32:04.310Z","0.3.0":"2026-05-05T17:47:37.136Z","0.3.1":"2026-05-11T02:25:14.915Z"},"bugs":{"url":"https://gitlab.com/johncandrew/beach/-/issues"},"license":"Apache-2.0","homepage":"https://cool-ai.org","repository":{"url":"git+https://gitlab.com/johncandrew/beach.git","type":"git","directory":"packages/transport-email"},"description":"IMAP and SMTP transport adapters for Beach — the wire layer underneath @cool-ai/beach-channel-email.","maintainers":[{"name":"johncandrew","email":"johncandrew@yahoo.co.uk"}],"readme":"# @cool-ai/beach-transport-email\n\n> **Home:** [cool-ai.org](https://cool-ai.org) · **Documentation:** [cool-ai.org/docs](https://cool-ai.org/docs)\n\nIMAP and SMTP transport adapters for Beach. Wire layer only — connection management, polling, parsing, sending. Channel-shaped concerns (envelope translation, threading, the `Missive` part-bag) live in [`@cool-ai/beach-channel-email`](../channel-email/README.md).\n\n## When to use this package directly\n\n- You are writing a custom email channel layered on a different missive shape.\n- You need to swap a formatter on the SMTP adapter without taking the whole channel package.\n- You are reimplementing parts of `@cool-ai/beach-channel-email` and only want the transport pieces.\n\nIf any of those does not match, use `@cool-ai/beach-channel-email` instead — it bundles these adapters with the canonical envelope-to-message translation and is what most consumers want.\n\n## Quickstart\n\n```ts\nimport { ImapInboundAdapter, SmtpOutboundAdapter } from '@cool-ai/beach-transport-email';\n\nconst inbound = new ImapInboundAdapter({\n  config: {\n    host: 'imap.example.com',\n    port: 993,\n    secure: true,\n    auth: { user: process.env.IMAP_USER!, pass: process.env.IMAP_PASS! },\n  },\n  pollIntervalMs: 60_000,\n  uidState: {\n    get: (mailbox) => redis.get(`beach-email:lastUid:${mailbox}`).then((v) => (v ? Number(v) : undefined)),\n    set: (mailbox, uid) => { await redis.set(`beach-email:lastUid:${mailbox}`, String(uid)); },\n  },\n  onMessage: async (parsed) => {\n    /* parsed is RFC 822-shaped — translate to a Missive in your channel layer */\n  },\n});\n\nawait inbound.start();\n\nconst outbound = new SmtpOutboundAdapter({\n  host: 'smtp.example.com',\n  port: 465,\n  secure: true,\n  auth: { user: process.env.SMTP_USER!, pass: process.env.SMTP_PASS! },\n  from: 'replies@example.com',\n});\n\nawait outbound.send({\n  to: ['someone@example.org'],\n  subject: 'Hello',\n  text: 'World',\n});\n```\n\n## What this package contains\n\n- **`ImapInboundAdapter`** — long-lived IMAP connection, configurable retrieval mode (poll or IDLE), UID-state callback, RFC 822 parsing via `mailparser`. Hands a `ParsedInboundEmail` to your `onMessage` callback.\n- **`SmtpOutboundAdapter`** — `nodemailer` transporter, single `send(email)` entry point. Owns the connection lifecycle.\n\nThat is the whole surface.\n\n## Retrieval modes\n\n`ImapInboundAdapter` supports two modes; pick the one that fits your latency budget.\n\n### `mode: 'poll'` (default)\n\nPeriodic UID-search every `pollIntervalMs`. Default 60 s. Works against every IMAP server. Simple, predictable, latency-bounded by the polling interval. The shape that has shipped since v1.\n\n```ts\nconst inbound = new ImapInboundAdapter({\n  config: { /* ... */ },\n  pollIntervalMs: 60_000,   // optional; this is the default\n  uidState,\n  onMessage,\n});\n```\n\n### `mode: 'idle'`\n\nLong-lived IMAP IDLE connection (RFC 2177). The server pushes `EXISTS` notifications when new messages arrive; latency is sub-second. Suitable for conversational email flows where one-minute polling latency is noticeable.\n\n```ts\nconst inbound = new ImapInboundAdapter({\n  config: { /* ... */ },\n  mode: 'idle',\n  idleRefreshMs: 25 * 60_000,   // optional; this is the default. ≤29 minutes per RFC 2177.\n  uidState,\n  onMessage,\n});\n```\n\n**Server requirements.** Every modern IMAP server advertises the `IDLE` capability — Microsoft Exchange, Gmail, Fastmail, Postfix-Dovecot, etc. The adapter checks `serverInfo.capabilities` after connect; if `IDLE` isn't advertised, it logs a warning and falls back to polling. Set `mode: 'poll'` explicitly to silence the warning.\n\n**Refresh interval.** The adapter periodically breaks IDLE and re-enters it to prevent server-side timeout. RFC 2177 mandates ≤29 minutes; the default of 25 minutes leaves headroom. Some hosted servers drop IDLE connections more aggressively — set `idleRefreshMs` lower if your provider documents a shorter limit.\n\n**Reconnection.** On connection drop or IDLE error, the adapter logs the error, drops the client, and reconnects with a 2-second backoff. The next iteration's connect re-opens the mailbox and re-enters IDLE. UID state is durable across reconnects (you supply the storage), so messages aren't double-delivered.\n\n**Initial fetch.** On `start()`, the adapter performs one poll-style UID search before entering IDLE. This catches messages that arrived between restarts. Mode-independent.\n\n## SMTP without authentication (trusted local relay)\n\n`SmtpTransportConfig.auth` is optional. When omitted the adapter connects without authentication — suitable only for **trusted-network local relays** such as a Postfix daemon on `localhost` accepting unauthenticated connections from the loopback interface and signing outbound with DKIM via OpenDKIM.\n\n```ts\nconst outbound = new SmtpOutboundAdapter({\n  host: 'localhost',\n  port: 25,\n  secure: false,\n  from: 'replies@example.com',\n});\n```\n\nThe adapter emits a startup warning when `auth` is absent so the opt-out is visible in logs. **Never use this mode against a remote SMTP server** — anything on the network can relay through it. The IMAP side still requires auth; only the SMTP path supports the no-auth shape.\n","readmeFilename":"README.md"}