@groundmail/groundhog 0.1.1 → 0.1.2
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/README.md +12 -6
- package/dist/ui/app.js +5 -3
- package/dist/ui/favicon.svg +1 -1
- package/dist/ui/index.html +2 -3
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
<p align="center"><img src="docs/groundhog.svg" width="360" alt="
|
|
1
|
+
<p align="center"><img src="docs/groundhog.svg" width="360" alt="Groundbot, the robot groundhog, peeking out of its burrow with happy eyes and an envelope on its chest light"></p>
|
|
2
2
|
|
|
3
3
|
<h1 align="center">Groundhog</h1>
|
|
4
4
|
|
|
5
5
|
<p align="center">Catch every email your app sends while you build. Nothing leaves your laptop.<br>
|
|
6
|
-
An open-source local inbox from <a href="https://groundmail.dev/?utm_source=
|
|
6
|
+
An open-source local inbox from <a href="https://groundmail.dev/?utm_source=github&utm_medium=readme&utm_campaign=groundhog&utm_content=header">Groundmail</a>.</p>
|
|
7
7
|
|
|
8
8
|
---
|
|
9
9
|
|
|
@@ -23,6 +23,10 @@ email it sends shows up at `http://localhost:3025`: sign-up codes, password rese
|
|
|
23
23
|
- **For tests and agents**: `waitForEmail()` for Playwright/Vitest, and an MCP server so coding
|
|
24
24
|
agents can read the verification email themselves.
|
|
25
25
|
|
|
26
|
+
<p align="center"><img src="docs/screenshot-inbox.png" width="900" alt="The Groundhog inbox: three caught emails (a Resend sign-up code, an SMTP password reset and a SendGrid receipt), with the verification email open and its one-time code 482913 and confirm link pulled out above the rendered HTML"></p>
|
|
27
|
+
|
|
28
|
+
<p align="center"><a href="docs/demo.gif">Watch a 15-second demo</a></p>
|
|
29
|
+
|
|
26
30
|
Coming from MailHog? Groundhog keeps SMTP on 1025; the inbox moves from 8025 to 3025.
|
|
27
31
|
|
|
28
32
|
## Start it
|
|
@@ -228,6 +232,8 @@ curl -X POST localhost:3025/api/faults -d '{"status":429,"next":3}'
|
|
|
228
232
|
|
|
229
233
|
A banner stays in the inbox while failure mode is on. `next: 0` or `DELETE /api/faults` turns it off.
|
|
230
234
|
|
|
235
|
+
<p align="center"><img src="docs/screenshot-failure-mode.png" width="760" alt="The Failure mode dialog: pick rate limited (429), invalid recipient (422), bad API key (401), provider outage (5xx) or a 30-second timeout, each with its SMTP reply code, and how many sends should fail"></p>
|
|
236
|
+
|
|
231
237
|
## Webhook simulation
|
|
232
238
|
|
|
233
239
|
Set your app's webhook URL in an email's **Webhooks** tab (or `PUT /api/webhooks`), then fire
|
|
@@ -252,7 +258,7 @@ public; never use them outside local development.
|
|
|
252
258
|
## Send to my real inbox
|
|
253
259
|
|
|
254
260
|
Some things only show up in a real mail client. Open an email, click **Send to my real inbox**,
|
|
255
|
-
and connect a free [Groundmail account](https://app.groundmail.dev/signup?utm_source=
|
|
261
|
+
and connect a free [Groundmail account](https://app.groundmail.dev/signup?utm_source=github&utm_medium=readme&utm_campaign=groundhog&utm_content=real_inbox)
|
|
256
262
|
once. The sign-in opens in your browser; there's no key to paste. Copies come from Groundmail's
|
|
257
263
|
sandbox sender, which only delivers to your own account address until you verify a domain, so
|
|
258
264
|
Groundhog can't email real users by mistake. The sign-in is saved in `~/.config/groundhog` and
|
|
@@ -293,7 +299,7 @@ Groundhog never relays mail. Nothing it catches is sent anywhere unless you clic
|
|
|
293
299
|
|
|
294
300
|
## Ready for real inboxes?
|
|
295
301
|
|
|
296
|
-
When it's time to ship, [Groundmail](https://groundmail.dev/?utm_source=
|
|
302
|
+
When it's time to ship, [Groundmail](https://groundmail.dev/?utm_source=github&utm_medium=readme&utm_campaign=groundhog&utm_content=go_live)
|
|
297
303
|
sends your app's email for real: an HTTP API and SMTP, with transactional mail on its own servers
|
|
298
304
|
and IPs. The free plan includes 3,000 emails a month. If you use the Groundmail SDK, going live is
|
|
299
305
|
two environment variables:
|
|
@@ -302,7 +308,7 @@ two environment variables:
|
|
|
302
308
|
GROUNDMAIL_API_KEY=gm_live_… # and drop GROUNDMAIL_BASE_URL
|
|
303
309
|
```
|
|
304
310
|
|
|
305
|
-
[Start free on Groundmail →](https://app.groundmail.dev/signup?utm_source=
|
|
311
|
+
[Start free on Groundmail →](https://app.groundmail.dev/signup?utm_source=github&utm_medium=readme&utm_campaign=groundhog&utm_content=go_live)
|
|
306
312
|
|
|
307
313
|
## Development
|
|
308
314
|
|
|
@@ -317,7 +323,7 @@ npm run test:e2e # the inbox UI and the Playwright example
|
|
|
317
323
|
Releases are automatic: bump `version` in `package.json` and merge to `main`. The release
|
|
318
324
|
workflow publishes to npm (with provenance), tags the version, and pushes the Docker image.
|
|
319
325
|
|
|
320
|
-
Issues and pull requests are welcome.
|
|
326
|
+
Issues and pull requests are welcome; see [CONTRIBUTING.md](CONTRIBUTING.md). Report security issues as described in [SECURITY.md](SECURITY.md).
|
|
321
327
|
|
|
322
328
|
## License
|
|
323
329
|
|
package/dist/ui/app.js
CHANGED
|
@@ -27,10 +27,12 @@ function svg(markup) {
|
|
|
27
27
|
return template.content.firstElementChild
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
+
// Groundbot, the robot groundhog: asleep while the inbox is empty, happy when an email is caught,
|
|
31
|
+
// crossed-out eyes when a send fails.
|
|
30
32
|
const MASCOT = {
|
|
31
|
-
dozing: `<svg class="empty-art" viewBox="0 0 360 200" width="360" height="200" role="img" aria-label="
|
|
32
|
-
mail: `<svg viewBox="0 0 60 44" width="48" height="36" aria-hidden="true"><
|
|
33
|
-
shadow: `<svg viewBox="0 0 120 70" width="56" height="34" aria-hidden="true"><
|
|
33
|
+
dozing: `<svg class="empty-art" viewBox="0 0 360 200" width="360" height="200" role="img" aria-label="Groundbot asleep in its burrow"><rect x="0" y="150" width="360" height="14" fill="#4a3a29"/><rect x="0" y="164" width="360" height="12" fill="#3a2d20"/><rect x="0" y="176" width="360" height="12" fill="#2c2218"/><rect x="0" y="188" width="360" height="12" fill="#1f1811"/><ellipse cx="180" cy="152" rx="96" ry="18" fill="#0d0a07"/><g transform="translate(95 9.2) scale(0.85)"><rect x="88" y="6" width="5" height="22" fill="#241c15"/><circle cx="90.5" cy="8" r="7" fill="#5f432c" stroke="#241c15" stroke-width="3"/><rect x="34" y="30" width="22" height="26" rx="6" fill="#5f432c" stroke="#241c15" stroke-width="4"/><rect x="144" y="30" width="22" height="26" rx="6" fill="#5f432c" stroke="#241c15" stroke-width="4"/><circle cx="45" cy="43" r="3" fill="#d6bf98"/><circle cx="155" cy="43" r="3" fill="#d6bf98"/><rect x="44" y="26" width="112" height="104" rx="26" fill="#8b6643" stroke="#241c15" stroke-width="4"/><rect x="58" y="50" width="84" height="44" rx="12" fill="#0d0a07" stroke="#241c15" stroke-width="3"/><path d="M72 74 h20 M108 74 h20" fill="none" stroke="#5f432c" stroke-width="5" stroke-linecap="round"/><text x="150" y="22" font-family="Geist, ui-sans-serif, system-ui, sans-serif" font-size="18" font-weight="700" fill="#8b6643">z</text><text x="164" y="10" font-family="Geist, ui-sans-serif, system-ui, sans-serif" font-size="13" font-weight="700" fill="#b48f62">z</text><rect x="78" y="98" width="44" height="22" rx="10" fill="#e8c9a0" stroke="#241c15" stroke-width="3"/><rect x="94" y="100" width="12" height="7" rx="3" fill="#241c15"/><rect x="93" y="116" width="6" height="9" rx="1" fill="#fbf8f1" stroke="#241c15" stroke-width="2"/><rect x="101" y="116" width="6" height="9" rx="1" fill="#fbf8f1" stroke="#241c15" stroke-width="2"/><path d="M52 104h12M52 112h12M136 104h12M136 112h12" stroke="#5f432c" stroke-width="3" stroke-linecap="round"/><rect x="30" y="128" width="140" height="40" rx="8" fill="#241c15"/><rect x="84" y="136" width="32" height="22" rx="4" fill="#5f432c"/><path d="M87 139 L100 149 L113 139" fill="none" stroke="#241c15" stroke-width="2.5" stroke-linejoin="round"/></g></svg>`,
|
|
34
|
+
mail: `<svg viewBox="0 0 60 44" width="48" height="36" aria-hidden="true"><g transform="translate(5 1) scale(0.25)"><rect x="87.5" y="6" width="6" height="22" fill="#241c15"/><circle cx="90.5" cy="8" r="8" fill="#e27d4e" stroke="#241c15" stroke-width="3"/><rect x="34" y="30" width="22" height="26" rx="6" fill="#5f432c" stroke="#241c15" stroke-width="5"/><rect x="144" y="30" width="22" height="26" rx="6" fill="#5f432c" stroke="#241c15" stroke-width="5"/><circle cx="45" cy="43" r="4" fill="#d6bf98"/><circle cx="155" cy="43" r="4" fill="#d6bf98"/><rect x="44" y="26" width="112" height="104" rx="26" fill="#8b6643" stroke="#241c15" stroke-width="6"/><rect x="58" y="50" width="84" height="44" rx="12" fill="#0d0a07"/><path d="M71 78 Q82 62 93 78 M107 78 Q118 62 129 78" fill="none" stroke="#e9b48a" stroke-width="8" stroke-linecap="round"/><rect x="78" y="98" width="44" height="22" rx="10" fill="#e8c9a0" stroke="#241c15" stroke-width="3"/><rect x="94" y="100" width="12" height="7" rx="3" fill="#241c15"/><rect x="93" y="116" width="14" height="9" rx="1" fill="#fbf8f1" stroke="#241c15" stroke-width="2"/><rect x="30" y="128" width="140" height="40" rx="8" fill="#241c15"/><rect x="84" y="136" width="32" height="22" rx="4" fill="#9cb67f"/><path d="M87 139 L100 149 L113 139" fill="none" stroke="#241c15" stroke-width="2.5" stroke-linejoin="round"/></g></svg>`,
|
|
35
|
+
shadow: `<svg viewBox="0 0 120 70" width="56" height="34" aria-hidden="true"><g transform="translate(20.714 2) scale(0.393)"><rect x="87.5" y="6" width="6" height="22" fill="#241c15"/><circle cx="90.5" cy="8" r="8" fill="#e27d4e" stroke="#241c15" stroke-width="3"/><rect x="34" y="30" width="22" height="26" rx="6" fill="#5f432c" stroke="#241c15" stroke-width="5"/><rect x="144" y="30" width="22" height="26" rx="6" fill="#5f432c" stroke="#241c15" stroke-width="5"/><circle cx="45" cy="43" r="4" fill="#d6bf98"/><circle cx="155" cy="43" r="4" fill="#d6bf98"/><rect x="44" y="26" width="112" height="104" rx="26" fill="#8b6643" stroke="#241c15" stroke-width="6"/><rect x="58" y="50" width="84" height="44" rx="12" fill="#0d0a07"/><path d="M75 65 L89 79 M89 65 L75 79 M111 65 L125 79 M125 65 L111 79" fill="none" stroke="#e27d4e" stroke-width="8" stroke-linecap="round"/><rect x="78" y="98" width="44" height="22" rx="10" fill="#e8c9a0" stroke="#241c15" stroke-width="3"/><rect x="94" y="100" width="12" height="7" rx="3" fill="#241c15"/><rect x="93" y="116" width="14" height="9" rx="1" fill="#fbf8f1" stroke="#241c15" stroke-width="2"/><rect x="30" y="128" width="140" height="40" rx="8" fill="#241c15"/><rect x="84" y="136" width="32" height="22" rx="4" fill="#e27d4e"/><path d="M87 139 L100 149 L113 139" fill="none" stroke="#241c15" stroke-width="2.5" stroke-linejoin="round"/></g></svg>`,
|
|
34
36
|
}
|
|
35
37
|
|
|
36
38
|
const VIA = {
|
package/dist/ui/favicon.svg
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 40 40"><rect
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 40 40"><g transform="translate(-8.358 1) scale(0.284)"><rect x="88" y="6" width="5" height="22" fill="#241c15"/><circle cx="90.5" cy="8" r="8" fill="#e27d4e"/><rect x="44" y="26" width="112" height="104" rx="26" fill="#8b6643" stroke="#241c15" stroke-width="8"/><rect x="58" y="50" width="84" height="44" rx="12" fill="#0d0a07"/><circle cx="82" cy="72" r="9" fill="#e9b48a"/><circle cx="118" cy="72" r="9" fill="#e9b48a"/><rect x="88" y="110" width="24" height="18" rx="3" fill="#fbf8f1" stroke="#241c15" stroke-width="5"/></g></svg>
|
package/dist/ui/index.html
CHANGED
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
<div class="app">
|
|
13
13
|
<header class="top">
|
|
14
14
|
<div class="brand">
|
|
15
|
-
<svg viewBox="0 0 40 40" width="32" height="32" aria-hidden="true"><rect width="
|
|
15
|
+
<svg viewBox="0 0 40 40" width="32" height="32" aria-hidden="true"><g transform="translate(-7.737 1.555) scale(0.277)"><rect x="87.5" y="6" width="6" height="22" fill="#241c15"/><circle cx="90.5" cy="8" r="8" fill="#e27d4e" stroke="#241c15" stroke-width="3"/><rect x="34" y="30" width="22" height="26" rx="6" fill="#5f432c" stroke="#241c15" stroke-width="5"/><rect x="144" y="30" width="22" height="26" rx="6" fill="#5f432c" stroke="#241c15" stroke-width="5"/><circle cx="45" cy="43" r="4" fill="#d6bf98"/><circle cx="155" cy="43" r="4" fill="#d6bf98"/><rect x="44" y="26" width="112" height="104" rx="26" fill="#8b6643" stroke="#241c15" stroke-width="6"/><rect x="58" y="50" width="84" height="44" rx="12" fill="#0d0a07"/><circle cx="82" cy="72" r="8" fill="#e9b48a"/><circle cx="118" cy="72" r="8" fill="#e9b48a"/><rect x="78" y="98" width="44" height="22" rx="10" fill="#e8c9a0" stroke="#241c15" stroke-width="3"/><rect x="94" y="100" width="12" height="7" rx="3" fill="#241c15"/><rect x="93" y="116" width="14" height="9" rx="1" fill="#fbf8f1" stroke="#241c15" stroke-width="2"/></g></svg>
|
|
16
16
|
<span class="brand-name">Groundhog</span>
|
|
17
17
|
<span class="brand-from">from <a data-utm="header" href="https://groundmail.dev/?utm_source=groundhog&utm_medium=app&utm_campaign=header" target="_blank" rel="noopener">Groundmail</a></span>
|
|
18
18
|
</div>
|
|
@@ -56,8 +56,7 @@
|
|
|
56
56
|
<svg class="dialog-art" viewBox="0 0 720 150" aria-hidden="true">
|
|
57
57
|
<rect x="0" y="112" width="720" height="10" fill="#4a3a29"/><rect x="0" y="122" width="720" height="10" fill="#3a2d20"/><rect x="0" y="132" width="720" height="18" fill="#2c2218"/>
|
|
58
58
|
<ellipse cx="200" cy="114" rx="70" ry="12" fill="#0d0a07"/>
|
|
59
|
-
<path d="
|
|
60
|
-
<circle cx="190" cy="88" r="3.5" fill="#241c15"/><circle cx="210" cy="88" r="3.5" fill="#241c15"/><ellipse cx="200" cy="97" rx="4" ry="3" fill="#241c15"/>
|
|
59
|
+
<g transform="translate(150 30) scale(0.5)"><rect x="88" y="6" width="5" height="22" fill="#241c15"/><circle cx="90.5" cy="8" r="7" fill="#e27d4e" stroke="#241c15" stroke-width="3"/><rect x="34" y="30" width="22" height="26" rx="6" fill="#5f432c" stroke="#241c15" stroke-width="4"/><rect x="144" y="30" width="22" height="26" rx="6" fill="#5f432c" stroke="#241c15" stroke-width="4"/><circle cx="45" cy="43" r="3" fill="#d6bf98"/><circle cx="155" cy="43" r="3" fill="#d6bf98"/><rect x="44" y="26" width="112" height="104" rx="26" fill="#8b6643" stroke="#241c15" stroke-width="4"/><rect x="58" y="50" width="84" height="44" rx="12" fill="#0d0a07" stroke="#241c15" stroke-width="3"/><circle cx="82" cy="72" r="11" fill="#2a1a12"/><circle cx="118" cy="72" r="11" fill="#2a1a12"/><circle cx="86" cy="68" r="5.5" fill="#e9b48a"/><circle cx="122" cy="68" r="5.5" fill="#e9b48a"/><rect x="78" y="98" width="44" height="22" rx="10" fill="#e8c9a0" stroke="#241c15" stroke-width="3"/><rect x="94" y="100" width="12" height="7" rx="3" fill="#241c15"/><rect x="93" y="116" width="6" height="9" rx="1" fill="#fbf8f1" stroke="#241c15" stroke-width="2"/><rect x="101" y="116" width="6" height="9" rx="1" fill="#fbf8f1" stroke="#241c15" stroke-width="2"/><path d="M52 104h12M52 112h12M136 104h12M136 112h12" stroke="#5f432c" stroke-width="3" stroke-linecap="round"/><rect x="30" y="128" width="140" height="40" rx="8" fill="#241c15"/><rect x="84" y="136" width="32" height="22" rx="4" fill="#d6bf98"/><path d="M87 139 L100 149 L113 139" fill="none" stroke="#241c15" stroke-width="2.5" stroke-linejoin="round"/></g>
|
|
61
60
|
<path d="M244 92 C330 40 420 40 500 70" fill="none" stroke="#7d6e5f" stroke-width="2" stroke-dasharray="4 6"/>
|
|
62
61
|
<g transform="rotate(12 520 70)"><rect x="496" y="54" width="48" height="32" rx="4" fill="#fbf8f1"/><path d="M497 56 L520 72 L543 56" fill="none" stroke="#a3461f" stroke-width="2.5"/></g>
|
|
63
62
|
<path d="M556 62 l14 -6 M558 74 l16 0 M556 86 l14 6" stroke="#e27d4e" stroke-width="2.5" stroke-linecap="round"/>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@groundmail/groundhog",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"description": "A local email inbox for development. Catches SMTP and the HTTP send APIs of Groundmail, SendGrid, Postmark, Mailgun, Resend and Amazon SES. Nothing leaves your machine.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -52,7 +52,7 @@
|
|
|
52
52
|
"playwright"
|
|
53
53
|
],
|
|
54
54
|
"scripts": {
|
|
55
|
-
"build": "
|
|
55
|
+
"build": "node scripts/build.mjs clean && tsc -p . && node scripts/build.mjs ui",
|
|
56
56
|
"dev": "npm run build && node dist/cli.js",
|
|
57
57
|
"test": "vitest run",
|
|
58
58
|
"test:e2e": "playwright test",
|