Skip to content

Commit dc78ee7

Browse files
authored
Merge pull request #20 from dahlia/jmap
Add @upyo/jmap package for JMAP email transport
2 parents c6f685e + 4004930 commit dc78ee7

35 files changed

+4705
-49
lines changed

.github/workflows/main.yaml

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,10 @@ jobs:
2424
MAILPIT_SMTP_PORT: 1025
2525
MAILPIT_SMTP_SECURE: "false"
2626
MAILPIT_API_URL: http://localhost:8025
27+
STALWART_SESSION_URL: http://localhost:8080/jmap/session
28+
STALWART_USERNAME: test@mail.example.com
29+
STALWART_PASSWORD: password123
30+
STALWART_BASE_URL: http://localhost:8080
2731
MAILGUN_KEY: ${{ secrets.MAILGUN_KEY }}
2832
MAILGUN_FROM: ${{ secrets.MAILGUN_FROM }}
2933
MAILGUN_TO: ${{ secrets.MAILGUN_TO }}
@@ -44,6 +48,10 @@ jobs:
4448
- uses: actions/setup-node@v4
4549
with:
4650
node-version: latest
51+
- name: Start Stalwart
52+
run: docker compose -f packages/jmap/docker-compose.yml up -d
53+
- name: Setup Stalwart test accounts
54+
run: ./packages/jmap/scripts/setup-stalwart.sh
4755
- run: pnpm run -r test
4856

4957
test-deno:
@@ -68,6 +76,10 @@ jobs:
6876
MAILPIT_SMTP_PORT: 1025
6977
MAILPIT_SMTP_SECURE: "false"
7078
MAILPIT_API_URL: http://localhost:8025
79+
STALWART_SESSION_URL: http://localhost:8080/jmap/session
80+
STALWART_USERNAME: test@mail.example.com
81+
STALWART_PASSWORD: password123
82+
STALWART_BASE_URL: http://localhost:8080
7183
MAILGUN_KEY: ${{ secrets.MAILGUN_KEY }}
7284
MAILGUN_FROM: ${{ secrets.MAILGUN_FROM }}
7385
MAILGUN_TO: ${{ secrets.MAILGUN_TO }}
@@ -84,6 +96,10 @@ jobs:
8496
- uses: denoland/setup-deno@v2
8597
with:
8698
deno-version: v2.x
99+
- name: Start Stalwart
100+
run: docker compose -f packages/jmap/docker-compose.yml up -d
101+
- name: Setup Stalwart test accounts
102+
run: ./packages/jmap/scripts/setup-stalwart.sh
87103
- run: deno task test --coverage --junit-path=junit.xml
88104
- if: ${{ !cancelled() }}
89105
uses: codecov/test-results-action@v1
@@ -117,6 +133,10 @@ jobs:
117133
MAILPIT_SMTP_PORT: 1025
118134
MAILPIT_SMTP_SECURE: "false"
119135
MAILPIT_API_URL: http://localhost:8025
136+
STALWART_SESSION_URL: http://localhost:8080/jmap/session
137+
STALWART_USERNAME: test@mail.example.com
138+
STALWART_PASSWORD: password123
139+
STALWART_BASE_URL: http://localhost:8080
120140
MAILGUN_KEY: ${{ secrets.MAILGUN_KEY }}
121141
MAILGUN_FROM: ${{ secrets.MAILGUN_FROM }}
122142
MAILGUN_TO: ${{ secrets.MAILGUN_TO }}
@@ -137,6 +157,10 @@ jobs:
137157
- uses: oven-sh/setup-bun@v2
138158
with:
139159
bun-version: 1.2.22
160+
- name: Start Stalwart
161+
run: docker compose -f packages/jmap/docker-compose.yml up -d
162+
- name: Setup Stalwart test accounts
163+
run: ./packages/jmap/scripts/setup-stalwart.sh
140164
- run: pnpm run -r test:bun
141165

142166
check:

CHANGES.md

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,27 @@ To be released.
3636

3737
[#18]: https://github.com/dahlia/upyo/issues/18
3838

39+
### @upyo/jmap
40+
41+
- Added JMAP transport for sending emails via JMAP protocol. [[#10]]
42+
43+
JMAP (JSON Meta Application Protocol) is a modern, efficient protocol for
44+
email access and submission, defined in RFC 8620 (core) and RFC 8621 (mail).
45+
This transport provides:
46+
47+
- Automatic session discovery and caching
48+
- Automatic identity resolution from sender email
49+
- Bearer token authentication
50+
- Exponential backoff retry with configurable attempts
51+
- Request timeout and AbortSignal support
52+
- Text and HTML message content (multipart/alternative)
53+
- Priority headers (X-Priority, Importance)
54+
- Custom headers
55+
- File attachments via blob upload
56+
- Inline attachments (multipart/related)
57+
58+
[#10]: https://github.com/dahlia/upyo/issues/10
59+
3960
### @upyo/resend
4061

4162
- Added support for user-provided idempotency keys via `Message.idempotencyKey`.

README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ sending messages. The following is a list of the available packages:
6868
| ----------------------------------------------- | ------------------------------ | ------------------------------ | -------------------------------------------------- |
6969
| [@upyo/core](/packages/core/) | [JSR][jsr:@upyo/core] | [npm][npm:@upyo/core] | Shared types and interfaces for email messages |
7070
| [@upyo/smtp](/packages/smtp/) | [JSR][jsr:@upyo/smtp] | [npm][npm:@upyo/smtp] | SMTP transport |
71+
| [@upyo/jmap](/packages/jmap/) | [JSR][jsr:@upyo/jmap] | [npm][npm:@upyo/jmap] | [JMAP] transport (RFC 8620/8621) |
7172
| [@upyo/mailgun](/packages/mailgun/) | [JSR][jsr:@upyo/mailgun] | [npm][npm:@upyo/mailgun] | [Mailgun] transport |
7273
| [@upyo/plunk](/packages/plunk/) | [JSR][jsr:@upyo/plunk] | [npm][npm:@upyo/plunk] | [Plunk] transport |
7374
| [@upyo/resend](/packages/resend/) | [JSR][jsr:@upyo/resend] | [npm][npm:@upyo/resend] | [Resend] transport |
@@ -80,6 +81,8 @@ sending messages. The following is a list of the available packages:
8081
[npm:@upyo/core]: https://www.npmjs.com/package/@upyo/core
8182
[jsr:@upyo/smtp]: https://jsr.io/@upyo/smtp
8283
[npm:@upyo/smtp]: https://www.npmjs.com/package/@upyo/smtp
84+
[jsr:@upyo/jmap]: https://jsr.io/@upyo/jmap
85+
[npm:@upyo/jmap]: https://www.npmjs.com/package/@upyo/jmap
8386
[jsr:@upyo/mailgun]: https://jsr.io/@upyo/mailgun
8487
[npm:@upyo/mailgun]: https://www.npmjs.com/package/@upyo/mailgun
8588
[jsr:@upyo/plunk]: https://jsr.io/@upyo/plunk
@@ -94,6 +97,7 @@ sending messages. The following is a list of the available packages:
9497
[npm:@upyo/opentelemetry]: https://www.npmjs.com/package/@upyo/opentelemetry
9598
[jsr:@upyo/mock]: https://jsr.io/@upyo/mock
9699
[npm:@upyo/mock]: https://www.npmjs.com/package/@upyo/mock
100+
[JMAP]: https://jmap.io/
97101
[Mailgun]: https://www.mailgun.com/
98102
[Plunk]: https://www.useplunk.com/
99103
[Resend]: https://resend.com/

deno.lock

Lines changed: 23 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

docs/.vitepress/config.mts

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import llmstxt from "vitepress-plugin-llms";
1212

1313
const packages: readonly string[] = [
1414
"core",
15+
"jmap",
1516
"mailgun",
1617
"plunk",
1718
"resend",
@@ -22,13 +23,17 @@ const packages: readonly string[] = [
2223
const jsrRefPlugins: any[] = [];
2324

2425
for (const pkg of packages) {
25-
jsrRefPlugins.push(
26-
await jsrRef({
27-
package: `@upyo/${pkg}`,
28-
version: process.env.JSR_REF_VERSION ?? "unstable",
29-
cachePath: `.jsr-cache-${pkg}.json`,
30-
}),
31-
);
26+
try {
27+
jsrRefPlugins.push(
28+
await jsrRef({
29+
package: `@upyo/${pkg}`,
30+
version: process.env.JSR_REF_VERSION ?? "unstable",
31+
cachePath: `.jsr-cache-${pkg}.json`,
32+
}),
33+
);
34+
} catch {
35+
// Ignore errors for packages not yet published to JSR
36+
}
3237
}
3338

3439
let extraNav: { text: string; link: string }[] = [];
@@ -67,6 +72,7 @@ const NAV = [
6772
text: "Transports",
6873
items: [
6974
{ text: "SMTP", link: "/transports/smtp" },
75+
{ text: "JMAP", link: "/transports/jmap" },
7076
{ text: "Mailgun", link: "/transports/mailgun" },
7177
{ text: "Plunk", link: "/transports/plunk" },
7278
{ text: "Resend", link: "/transports/resend" },
@@ -83,6 +89,7 @@ const NAV = [
8389
items: [
8490
{ text: "@upyo/core", link: "https://jsr.io/@upyo/core/doc" },
8591
{ text: "@upyo/smtp", link: "https://jsr.io/@upyo/smtp/doc" },
92+
{ text: "@upyo/jmap", link: "https://jsr.io/@upyo/jmap/doc" },
8693
{ text: "@upyo/mailgun", link: "https://jsr.io/@upyo/mailgun/doc" },
8794
{ text: "@upyo/plunk", link: "https://jsr.io/@upyo/plunk/doc" },
8895
{ text: "@upyo/resend", link: "https://jsr.io/@upyo/resend/doc" },

docs/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
"@shikijs/vitepress-twoslash": "^3.20.0",
99
"@types/node": "^24.0.13",
1010
"@upyo/core": "workspace:",
11+
"@upyo/jmap": "workspace:",
1112
"@upyo/mailgun": "workspace:",
1213
"@upyo/mock": "workspace:",
1314
"@upyo/opentelemetry": "workspace:",

0 commit comments

Comments
 (0)