groundmail-cli 0.26.3 → 0.27.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.
Files changed (3) hide show
  1. package/README.md +15 -0
  2. package/dist/cli.js +79 -1
  3. package/package.json +2 -2
package/README.md CHANGED
@@ -63,6 +63,21 @@ SENDGRID_API_KEY=… groundmail migrate sendgrid --dry-run # import from SendG
63
63
 
64
64
  Run `groundmail --help` or `groundmail <command> --help` for flags.
65
65
 
66
+ ## Email testing
67
+
68
+ Your team has a test domain (`<team>.test.groundmail.dev`). Mail sent there from any provider is
69
+ caught and never delivered. Get a fresh address, have your app send to it, then wait for it:
70
+
71
+ ```bash
72
+ TO=$(groundmail test-inbox address --prefix signup) # signup-k3m9x2pq@acme.test.groundmail.dev
73
+ # …trigger your app's signup with $TO…
74
+ groundmail test-inbox wait --to "$TO" --timeout 30 # prints the subject, code and links
75
+ groundmail test-inbox wait --to "$TO" --json | jq -r .email.code
76
+ groundmail test-inbox list --to "$TO"
77
+ ```
78
+
79
+ `wait` exits 2 when nothing arrives in time. Docs: https://docs.groundmail.dev/email-testing
80
+
66
81
  ## Tenants
67
82
 
68
83
  If you send email for your own customers, give each one a tenant: its own domains,
package/dist/cli.js CHANGED
@@ -1452,7 +1452,7 @@ function trimUrl(value) {
1452
1452
  }
1453
1453
 
1454
1454
  // package.json
1455
- var version = "0.26.3";
1455
+ var version = "0.27.0";
1456
1456
 
1457
1457
  // src/user-agent.ts
1458
1458
  function buildUserAgent(own) {
@@ -3745,6 +3745,80 @@ Updated the DMARC record with ${result2.applied.provider}.`);
3745
3745
  }
3746
3746
  }
3747
3747
 
3748
+ // src/commands/test-inbox/test-inbox.ts
3749
+ function readFlag24(args, flag) {
3750
+ const index = args.indexOf(flag);
3751
+ if (index === -1) return void 0;
3752
+ return args[index + 1];
3753
+ }
3754
+ async function createTestInboxClient(apiKey, baseUrl) {
3755
+ const { Groundmail } = await import("groundmail");
3756
+ return new Groundmail(apiKey, baseUrl ? { baseUrl } : void 0);
3757
+ }
3758
+ function formatTestEmail(email) {
3759
+ const lines = [
3760
+ `Subject: ${email.subject}`,
3761
+ `From: ${email.from}`,
3762
+ `To: ${email.to.join(", ")}`,
3763
+ `Code: ${email.code ?? "(none found)"}`
3764
+ ];
3765
+ if (email.links.length) {
3766
+ lines.push("Links:");
3767
+ for (const link of email.links) lines.push(` ${link}`);
3768
+ } else {
3769
+ lines.push("Links: (none)");
3770
+ }
3771
+ lines.push(`Id: ${email.id}`);
3772
+ return lines.join("\n");
3773
+ }
3774
+ var USAGE = "Usage: groundmail test-inbox address [--prefix signup] | wait --to <address> [--timeout 30] [--since <iso>] [--json] | list [--to <address>] [--json]";
3775
+ async function runTestInboxCommand(subcommand, args, env = process.env) {
3776
+ if (!subcommand || !["address", "wait", "list"].includes(subcommand)) return { exitCode: 1, error: USAGE };
3777
+ const apiKey = readFlag24(args, "--api-key") || env.GROUNDMAIL_API_KEY;
3778
+ if (!apiKey) return { exitCode: 1, error: "Missing API key. Run groundmail login, set GROUNDMAIL_API_KEY or pass --api-key." };
3779
+ const json = args.includes("--json");
3780
+ try {
3781
+ const client2 = await createTestInboxClient(apiKey, readFlag24(args, "--base-url"));
3782
+ if (subcommand === "address") {
3783
+ console.log(await client2.testInbox.address({ prefix: readFlag24(args, "--prefix") }));
3784
+ return { exitCode: 0 };
3785
+ }
3786
+ if (subcommand === "list") {
3787
+ const result = await client2.testInbox.list({
3788
+ to: readFlag24(args, "--to"),
3789
+ since: readFlag24(args, "--since"),
3790
+ limit: readFlag24(args, "--limit") ? Number(readFlag24(args, "--limit")) : void 0
3791
+ });
3792
+ if (json) console.log(JSON.stringify(result));
3793
+ else if (!result.emails.length) console.log("No test emails yet.");
3794
+ else {
3795
+ for (const email of result.emails) {
3796
+ console.log(`${email.received_at} ${email.id} ${email.to.join(", ")} ${email.subject}${email.code ? ` code ${email.code}` : ""}`);
3797
+ }
3798
+ }
3799
+ return { exitCode: 0 };
3800
+ }
3801
+ const to = readFlag24(args, "--to");
3802
+ if (!to) return { exitCode: 1, error: "Missing --to. Usage: groundmail test-inbox wait --to signup-k3m9x2pq@acme.test.groundmail.dev" };
3803
+ const timeoutRaw = readFlag24(args, "--timeout");
3804
+ const seconds = timeoutRaw === void 0 ? 30 : Number(timeoutRaw);
3805
+ if (!Number.isFinite(seconds) || seconds < 0) return { exitCode: 1, error: "--timeout must be a number of seconds" };
3806
+ try {
3807
+ const email = await client2.testInbox.waitFor({ to, timeout: seconds * 1e3, since: readFlag24(args, "--since") });
3808
+ console.log(json ? JSON.stringify({ email }) : formatTestEmail(email));
3809
+ return { exitCode: 0 };
3810
+ } catch (error) {
3811
+ const status = error?.status;
3812
+ if (status === 408) {
3813
+ return { exitCode: 2, error: `No email to ${to} arrived within ${seconds} seconds.` };
3814
+ }
3815
+ throw error;
3816
+ }
3817
+ } catch (error) {
3818
+ return { exitCode: 1, error: error instanceof Error ? error.message : String(error) };
3819
+ }
3820
+ }
3821
+
3748
3822
  // src/help.ts
3749
3823
  var USAGE_LINES = [
3750
3824
  " groundmail login [--scope <scopes>] [--workspace <name>] sign in through the browser (OAuth; no key to copy)",
@@ -3768,6 +3842,9 @@ var USAGE_LINES = [
3768
3842
  " groundmail dmarc disable example.com",
3769
3843
  " groundmail received-emails list [--domain inbound.example.com]",
3770
3844
  " groundmail received-emails get inb_...",
3845
+ " groundmail test-inbox address [--prefix signup] a fresh address on your team's test domain (mail there is caught, never delivered)",
3846
+ " groundmail test-inbox wait --to <address> [--timeout 30] [--since <iso>] [--json] wait for an email; prints its code and links (exit 2 on timeout)",
3847
+ " groundmail test-inbox list [--to <address>] [--since <iso>] [--json]",
3771
3848
  ' groundmail emails send --from you@domain.com --to user@example.com --subject "Hello" --html "<p>Hi</p>" [--category transactional|product|newsletter] [--unsubscribe] [--tenant <tenant>]',
3772
3849
  " groundmail emails batch --file ./emails.json [--tenant <tenant>]",
3773
3850
  " groundmail emails list [--limit 25] [--cursor \u2026] [--test | --live] [--tenant <tenant>] [--stream transactional|product|newsletter]",
@@ -3923,6 +4000,7 @@ async function main() {
3923
4000
  if (command === "migrate") await finish(await runMigrateCommand(subcommand, rest));
3924
4001
  if (command === "validate") await finish(await runValidateCommand([...subcommand ? [subcommand] : [], ...rest]));
3925
4002
  if (command === "dmarc") await finish(await runDmarcCommand(subcommand, rest));
4003
+ if (command === "test-inbox") await finish(await runTestInboxCommand(subcommand, rest));
3926
4004
  if (command === "domains") {
3927
4005
  if (subcommand === "add" || subcommand === "create") {
3928
4006
  const result = await runAddCommand(rest);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "groundmail-cli",
3
- "version": "0.26.3",
3
+ "version": "0.27.0",
4
4
  "description": "Groundmail CLI — send transactional email from the terminal",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -38,7 +38,7 @@
38
38
  "prepublishOnly": "npm run build"
39
39
  },
40
40
  "dependencies": {
41
- "groundmail": "0.25.1"
41
+ "groundmail": "0.26.0"
42
42
  },
43
43
  "devDependencies": {
44
44
  "tsup": "^8.5.0",