groundmail-cli 0.34.0 → 0.35.1

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 +1 -0
  2. package/dist/cli.js +110 -8
  3. package/package.json +2 -2
package/README.md CHANGED
@@ -75,6 +75,7 @@ groundmail domains list
75
75
  groundmail domains verify example.com
76
76
  groundmail webhooks list
77
77
  groundmail emails list
78
+ groundmail brand set --logo-url https://cdn.acme.com/logo.png --color '#2f6feb' --company "Acme Inc" # logo, color and footer every template follows
78
79
  groundmail senders list # saved senders; send with --sender billing instead of --from
79
80
  SENDGRID_API_KEY=… groundmail migrate sendgrid --dry-run # import from SendGrid, Postmark, Mailgun, Resend or Amazon SES (beta)
80
81
  ```
package/dist/cli.js CHANGED
@@ -2055,7 +2055,7 @@ function trimUrl(value) {
2055
2055
  }
2056
2056
 
2057
2057
  // package.json
2058
- var version = "0.34.0";
2058
+ var version = "0.35.1";
2059
2059
 
2060
2060
  // src/user-agent.ts
2061
2061
  function buildUserAgent(own) {
@@ -2082,6 +2082,12 @@ function fetchWithUserAgent(userAgent) {
2082
2082
 
2083
2083
  // src/client-info.ts
2084
2084
  var CLI_PACKAGE = { name: "groundmail-cli", version };
2085
+ function sdkOptions(baseUrl) {
2086
+ return {
2087
+ ...baseUrl ? { baseUrl } : {},
2088
+ fetch: fetchWithUserAgent(buildUserAgent(CLI_PACKAGE))
2089
+ };
2090
+ }
2085
2091
 
2086
2092
  // src/session/http.ts
2087
2093
  var CliRequestError = class extends Error {
@@ -3133,7 +3139,7 @@ function resolveApiKey8(options, env) {
3133
3139
  }
3134
3140
  async function createTemplatesClient(apiKey, baseUrl) {
3135
3141
  const { Groundmail } = await import("groundmail");
3136
- return new Groundmail(apiKey, baseUrl ? { baseUrl } : void 0);
3142
+ return new Groundmail(apiKey, sdkOptions(baseUrl));
3137
3143
  }
3138
3144
 
3139
3145
  // src/commands/templates/delete.ts
@@ -3283,8 +3289,8 @@ function extensionOf(filePath) {
3283
3289
  return match ? match[0].toLowerCase() : "";
3284
3290
  }
3285
3291
  async function defaultReadFile(filePath) {
3286
- const { readFile: readFile3 } = await import("fs/promises");
3287
- return readFile3(filePath, "utf8");
3292
+ const { readFile: readFile4 } = await import("fs/promises");
3293
+ return readFile4(filePath, "utf8");
3288
3294
  }
3289
3295
  async function defaultImportModule(filePath) {
3290
3296
  const { pathToFileURL } = await import("url");
@@ -3335,14 +3341,14 @@ async function toElement(mod, props) {
3335
3341
  return exported;
3336
3342
  }
3337
3343
  async function loadTemplateSource(filePath, options = {}, deps = {}) {
3338
- const readFile3 = deps.readFile ?? defaultReadFile;
3344
+ const readFile4 = deps.readFile ?? defaultReadFile;
3339
3345
  const importModule = deps.importModule ?? defaultImportModule;
3340
3346
  const extension = extensionOf(filePath);
3341
3347
  if (HTML_EXTENSIONS.includes(extension)) {
3342
- return { html: await readFile3(filePath) };
3348
+ return { html: await readFile4(filePath) };
3343
3349
  }
3344
3350
  if (TEXT_EXTENSIONS.includes(extension)) {
3345
- return { text: await readFile3(filePath) };
3351
+ return { text: await readFile4(filePath) };
3346
3352
  }
3347
3353
  if (!MODULE_EXTENSIONS.includes(extension)) {
3348
3354
  throw new Error(
@@ -4064,6 +4070,95 @@ async function runDeleteCommand6(args, env = process.env) {
4064
4070
  return withClient2(args, env, (client2) => client2.senders.remove(sender));
4065
4071
  }
4066
4072
 
4073
+ // src/commands/brand/commands.ts
4074
+ import { readFile as readFile3 } from "fs/promises";
4075
+ async function createBrandClient(apiKey, baseUrl) {
4076
+ const { Groundmail } = await import("groundmail");
4077
+ return new Groundmail(apiKey, baseUrl ? { baseUrl } : void 0);
4078
+ }
4079
+ var HEX = /^#(?:[0-9a-f]{3}|[0-9a-f]{6})$/i;
4080
+ function readableText(hex) {
4081
+ const full = hex.length === 4 ? `#${hex[1]}${hex[1]}${hex[2]}${hex[2]}${hex[3]}${hex[3]}` : hex;
4082
+ const lin = (i) => {
4083
+ const c = parseInt(full.slice(i, i + 2), 16) / 255;
4084
+ return c <= 0.03928 ? c / 12.92 : ((c + 0.055) / 1.055) ** 2.4;
4085
+ };
4086
+ const luminance = 0.2126 * lin(1) + 0.7152 * lin(3) + 0.0722 * lin(5);
4087
+ return 1.05 / (luminance + 0.05) >= (luminance + 0.05) / (0.0103 + 0.05) ? "#ffffff" : "#1c1917";
4088
+ }
4089
+ async function withClient3(args, env, run) {
4090
+ const options = parseSendersCommonArgs(args);
4091
+ const apiKey = resolveApiKey10(options, env);
4092
+ if (!apiKey) return { exitCode: 1, error: MISSING_KEY2 };
4093
+ try {
4094
+ const client2 = await createBrandClient(apiKey, options.baseUrl);
4095
+ console.log(JSON.stringify(await run(client2)));
4096
+ return { exitCode: 0 };
4097
+ } catch (error) {
4098
+ return failure2(error);
4099
+ }
4100
+ }
4101
+ async function runGetCommand9(args, env = process.env) {
4102
+ return withClient3(args, env, (client2) => client2.brand.get());
4103
+ }
4104
+ function clearable(value) {
4105
+ if (value === void 0) return void 0;
4106
+ return value === "none" || value === "" ? null : value;
4107
+ }
4108
+ async function runSetCommand(args, env = process.env) {
4109
+ const logoUrl = clearable(readFlag22(args, "--logo-url"));
4110
+ const logoAlign = readFlag22(args, "--logo-align");
4111
+ const logoWidth = readFlag22(args, "--logo-width");
4112
+ const color = readFlag22(args, "--color");
4113
+ const settingsFile = readFlag22(args, "--settings-file");
4114
+ if (logoAlign !== void 0 && logoAlign !== "left" && logoAlign !== "center") {
4115
+ return { exitCode: 1, error: "--logo-align must be left or center" };
4116
+ }
4117
+ if ((logoAlign !== void 0 || logoWidth !== void 0) && !logoUrl) {
4118
+ return { exitCode: 1, error: "--logo-align and --logo-width go with --logo-url" };
4119
+ }
4120
+ if (logoWidth !== void 0 && !/^\d+$/.test(logoWidth)) return { exitCode: 1, error: "--logo-width must be a whole number of px" };
4121
+ if (color !== void 0 && !HEX.test(color)) return { exitCode: 1, error: "--color must be a hex colour such as #2f6feb" };
4122
+ let settingsFromFile;
4123
+ if (settingsFile) {
4124
+ try {
4125
+ settingsFromFile = JSON.parse(await readFile3(settingsFile, "utf8"));
4126
+ } catch {
4127
+ return { exitCode: 1, error: `Could not read JSON from ${settingsFile}` };
4128
+ }
4129
+ }
4130
+ const params = {};
4131
+ if (logoUrl !== void 0) {
4132
+ params.logo = logoUrl === null ? null : { url: logoUrl, align: logoAlign ?? "left", ...logoWidth ? { width: Number(logoWidth) } : {} };
4133
+ }
4134
+ const company = clearable(readFlag22(args, "--company"));
4135
+ const address = clearable(readFlag22(args, "--address"));
4136
+ const website = clearable(readFlag22(args, "--website"));
4137
+ if (company !== void 0) params.company_name = company;
4138
+ if (address !== void 0) params.address = address;
4139
+ if (website !== void 0) params.website = website;
4140
+ if (settingsFromFile) params.settings = settingsFromFile;
4141
+ if (!Object.keys(params).length && !color) {
4142
+ return {
4143
+ exitCode: 1,
4144
+ error: "Nothing to set. Usage: groundmail brand set [--logo-url \u2026] [--color #2f6feb] [--company \u2026] [--address \u2026] [--website \u2026] [--settings-file brand.json]"
4145
+ };
4146
+ }
4147
+ return withClient3(args, env, async (client2) => {
4148
+ if (color) {
4149
+ const base = params.settings ?? (await client2.brand.get()).brand.settings ?? {};
4150
+ const theme = base.theme ?? {};
4151
+ params.accent_color = color;
4152
+ params.settings = {
4153
+ ...base,
4154
+ linkColor: color,
4155
+ theme: { ...theme, buttonBackground: color, buttonTextColor: readableText(color) }
4156
+ };
4157
+ }
4158
+ return client2.brand.update(params);
4159
+ });
4160
+ }
4161
+
4067
4162
  // src/commands/auth.ts
4068
4163
  async function runAuthCommand(subcommand, argv, deps = {}) {
4069
4164
  const env = deps.env ?? process.env;
@@ -4694,6 +4789,8 @@ var USAGE_LINES = [
4694
4789
  " groundmail tenants domains list <tenant>",
4695
4790
  " groundmail tenants domains add <tenant> example.com only this tenant can then send from it",
4696
4791
  " groundmail tenants domains remove <tenant> example.com gives the domain back to the account",
4792
+ " groundmail brand get your logo, colors and footer, which templates follow",
4793
+ ' groundmail brand set [--logo-url <https url>|none] [--logo-align left|center] [--logo-width 120] [--color #2f6feb] [--company "Acme Inc"|none] [--address "\u2026"|none] [--website acme.com|none] [--settings-file brand.json] --color: buttons, links and the line across the top',
4697
4794
  " groundmail senders list the names and addresses your email goes out as",
4698
4795
  ' groundmail senders create --name "Acme Billing" --address billing@mail.acme.com [--reply-to billing@acme.com] [--handle billing] [--default] address on a verified domain',
4699
4796
  " groundmail senders get <id|handle>",
@@ -4742,7 +4839,8 @@ var ALIASES = {
4742
4839
  suppressions: { create: "add", delete: "remove" },
4743
4840
  templates: { add: "create", remove: "delete" },
4744
4841
  tenants: { add: "create", remove: "delete" },
4745
- senders: { add: "create", remove: "delete" }
4842
+ senders: { add: "create", remove: "delete" },
4843
+ brand: { update: "set" }
4746
4844
  };
4747
4845
  function words(line) {
4748
4846
  return line.trim().split(/\s+/);
@@ -5080,6 +5178,10 @@ async function main() {
5080
5178
  if (subcommand === "stats") await finish(await runStatsCommand(rest));
5081
5179
  if (subcommand === "domains") await finish(await runDomainsCommand(rest));
5082
5180
  }
5181
+ if (command === "brand") {
5182
+ if (subcommand === "get") await finish(await runGetCommand9(rest));
5183
+ if (subcommand === "set" || subcommand === "update") await finish(await runSetCommand(rest));
5184
+ }
5083
5185
  if (command === "senders") {
5084
5186
  if (subcommand === "list") await finish(await runListCommand9(rest));
5085
5187
  if (subcommand === "create" || subcommand === "add") await finish(await runCreateCommand5(rest));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "groundmail-cli",
3
- "version": "0.34.0",
3
+ "version": "0.35.1",
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.36.0"
41
+ "groundmail": "0.37.0"
42
42
  },
43
43
  "devDependencies": {
44
44
  "tsup": "^8.5.0",