Conversation
Co-Authored-By: Claude <[email protected]>
Co-Authored-By: Claude <[email protected]>
Co-Authored-By: Claude <[email protected]>
Co-Authored-By: Claude <[email protected]>
Co-Authored-By: Claude <[email protected]>
Co-Authored-By: Claude <[email protected]>
Co-Authored-By: Claude <[email protected]>
- Add package README with installation, usage, and configuration docs - Add VitePress documentation page for JMAP transport - Update root README with @upyo/jmap in packages table - Update CHANGES.md with @upyo/jmap changelog entry - Add @upyo/jmap to docs devDependencies for twoslash type checking - Make jsrRef plugin errors silent for unpublished packages Co-Authored-By: Claude <[email protected]>
- Remove unnecessary async keywords from test fetch mocks - Replace async with Promise.resolve() for lint compliance - Fix unused variable by prefixing with underscore - Format code with deno fmt Co-Authored-By: Claude <[email protected]>
Co-Authored-By: Claude <[email protected]>
Co-Authored-By: Claude <[email protected]>
Co-Authored-By: Claude <[email protected]>
- Add E2E tests with Stalwart Mail Server for real JMAP testing - Add docker-compose.yml with fixed admin password for consistent testing - Add scripts/setup-stalwart.sh for automated user/domain setup - Add basicAuth and baseUrl config options for Docker environments - Add test-utils/test-config.ts for E2E test configuration - Run E2E tests sequentially to avoid race conditions Co-Authored-By: Claude <[email protected]>
TypeScript 5.9.2 (shipped with Deno 2.6.3) no longer considers Uint8Array directly assignable to BlobPart. This fixes the type error by extracting the underlying ArrayBuffer with slice() before creating the Blob. Co-Authored-By: Claude <[email protected]>
Use docker-compose to run Stalwart Mail Server in GitHub Actions CI. This enables E2E testing of the JMAP transport against a real JMAP server. Co-Authored-By: Claude <[email protected]>
Instead of sending emails sequentially, sendMany() now batches multiple messages into a single JMAP request with multiple Email/set creates and EmailSubmission/set creates. This reduces HTTP round-trips and improves throughput when sending multiple emails. Co-Authored-By: Claude <[email protected]>
Closed
Welcome to Codecov 🎉Once you merge this PR into your default branch, you're all set! Codecov will compare coverage reports and display results in all future pull requests. Thanks for integrating Codecov - We've got you covered ☂️ |
There was a problem hiding this comment.
Pull request overview
This PR introduces the @upyo/jmap package, implementing email transport via the JMAP protocol (RFC 8620/8621). JMAP is a modern JSON-based protocol designed as an alternative to SMTP/IMAP, offering stateless HTTP communication and batch request capabilities well-suited for serverless environments.
Key Changes:
- Complete JMAP transport implementation with session discovery, identity resolution, and batch processing
- Comprehensive test coverage including unit tests and E2E tests against Stalwart Mail Server
- Full documentation including package README and VitePress guide
Reviewed changes
Copilot reviewed 33 out of 35 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
packages/jmap/src/*.ts |
Core transport implementation with config, HTTP client, session management, message conversion, and blob upload |
packages/jmap/src/*.test.ts |
Comprehensive unit and E2E test suites |
packages/jmap/README.md |
Package documentation with installation and usage examples |
docs/transports/jmap.md |
VitePress documentation page with detailed configuration guide |
packages/jmap/docker-compose.yml |
Stalwart Mail Server setup for E2E testing |
.github/workflows/main.yaml |
CI integration for automated testing |
CHANGES.md |
Changelog entry for the new package |
pnpm-lock.yaml |
Dependency updates including jmap-rfc-types |
Files not reviewed (1)
- pnpm-lock.yaml: Language not supported
This allows timeout: 0 (instant timeout) and retries: 0 (no retries) to be valid configurations instead of being replaced with defaults. #20 (comment) Co-Authored-By: Claude <[email protected]>
Attachments are fully implemented, not planned for future release. #20 (comment) Co-Authored-By: Claude <[email protected]>
Now that config uses nullish coalescing, 0 is a valid value. #20 (comment) Co-Authored-By: Claude <[email protected]>
#20 (comment) Co-Authored-By: Claude <[email protected]>
Prefer AbortSignal.any() when available, with fallback to manual signal mirroring. Also preserve the abort reason when propagating. #20 (comment) Co-Authored-By: Claude <[email protected]>
Track processing stage (session fetch, mailbox discovery, identity resolution, attachment upload, message conversion, batch request execution) and provide detailed error messages that indicate where the failure occurred. For attachment upload failures, also report how many messages had their attachments uploaded before the failure. #20 (comment) Co-Authored-By: Claude <[email protected]>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
This PR adds
@upyo/jmap, a new transport package that implements email sending via the JMAP protocol (RFC 8620/8621). JMAP is a modern, JSON-based protocol designed to replace IMAP and offers advantages like stateless HTTP communication and batch request support.Closes #10.
Overview
Unlike SMTP which requires maintaining persistent connections, JMAP uses standard HTTP POST requests with JSON payloads. This makes it a natural fit for serverless and edge environments where connection pooling is impractical. The implementation follows the same patterns established by other HTTP-based transports in Upyo (Mailgun, SendGrid, SES).
A key feature of JMAP is its support for batch requests: multiple method calls can be combined into a single HTTP request, with later calls referencing results from earlier ones. The
sendMany()method takes advantage of this by batching all emails into a single request, significantly reducing HTTP round-trips when sending multiple messages.Usage
Features
The transport supports all standard Upyo message features including text/HTML content, attachments (both inline and regular), CC/BCC recipients, reply-to addresses, custom headers, and priority settings. Session information is cached to avoid redundant round-trips, and the transport automatically discovers the mail-capable account and resolves sender identities.
Comparison with original plan
The implementation follows the plan outlined in issue #10 with a few differences:
sendMany()The delayed send and undo features were intentionally deferred as they require additional protocol support that not all JMAP servers implement consistently.
Testing
The package includes comprehensive unit tests covering configuration, session management, message conversion, HTTP client behavior, and batch processing. E2E tests run against Stalwart Mail Server in Docker, which is automatically started in CI via docker-compose.
Documentation