wawesome 0.12.0 → 0.13.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/index.mjs +101 -21
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -117,6 +117,8 @@ npx wawesome deploy
117
117
  | `npx wawesome env list` | View environment variables for the current app |
118
118
  | `npx wawesome env set <key> <val>` | Set an environment variable (add `--secret` for write-only) |
119
119
  | `npx wawesome env rm <key>` | Delete an environment variable |
120
+ | `npx wawesome domains` | Show the app's domain, its state, and the DNS records to add |
121
+ | `npx wawesome domains detach <host>` | Stop serving the app at a domain you attached |
120
122
  | `npx wawesome credentials` | List deploy credentials: name, prefix, capabilities, Apps, last use |
121
123
  | `npx wawesome credentials mint <name>` | Mint a deploy credential and print its secret, once |
122
124
  | `npx wawesome credentials revoke <name>`| Revoke a deploy credential by name or prefix |
@@ -527,6 +529,19 @@ Your deploy waits for neither. It attaches the domain, prints the records and fi
527
529
  later deploy states where the domain got to: not verified, ownership verified, certificate issued, or
528
530
  serving. A domain that is already serving is a line saying so and nothing else.
529
531
 
532
+ The same block is there when you are not deploying:
533
+
534
+ ```bash
535
+ npx wawesome domains
536
+ ```
537
+
538
+ It prints the domain, where it got to, when the platform last checked, and the records to add. It
539
+ changes nothing, and a deploy credential carrying `deploy` can run it, so a pipeline reads the same
540
+ thing you do. The TXT record is minted a moment after the name is claimed, and an attach that outran
541
+ it prints a row saying so rather than dropping the record from the list. That row is what this
542
+ command fills in, and it is where the deploy sends you: deploying again would change nothing, and a
543
+ deploy that changes nothing is refused.
544
+
530
545
  A custom domain is granted from the Solo plan upwards, one per App. On a plan that grants none, the
531
546
  deploy prints the refusal and lands everything else it was doing.
532
547
 
package/dist/index.mjs CHANGED
@@ -883,7 +883,7 @@ async function buildJs(entryInput, options) {
883
883
  * that has to name this version — `--version`, the dependency a scaffolded
884
884
  * project pins — reads it here, so a release bumps one file.
885
885
  */
886
- const CLI_VERSION = "0.12.0";
886
+ const CLI_VERSION = "0.13.0";
887
887
  //#endregion
888
888
  //#region src/prompt.ts
889
889
  /**
@@ -1630,13 +1630,11 @@ async function detachDomain(creds, app, hostname) {
1630
1630
  });
1631
1631
  if (!res.ok) throw await asGatewayError(res, `'${hostname}' could not be detached (HTTP ${res.status}).`);
1632
1632
  }
1633
- /** A missing name is refused here rather than sent, so the reason reads at the keyboard. */
1634
- async function detachDomainCommand(hostname, options = {}) {
1635
- if (!hostname || !hostname.trim()) {
1636
- console.error("[wawesome] Error: Missing domain. Usage: wawesome domains detach <hostname>");
1637
- console.error("[wawesome] The domain is typed out because detaching one takes it dark.");
1638
- process.exit(1);
1639
- }
1633
+ /**
1634
+ * Who is asking and which App they are asking about. A deploy credential in the
1635
+ * environment is a caller here, so this never insists on a session.
1636
+ */
1637
+ function credentialsAndApp(options) {
1640
1638
  const creds = readCredentials();
1641
1639
  if (!creds) {
1642
1640
  console.error("[wawesome] Error: Not logged in. Run 'wawesome login' first.");
@@ -1648,6 +1646,19 @@ async function detachDomainCommand(hostname, options = {}) {
1648
1646
  console.error("[wawesome] Run this from your project, or name the app with --app <slug>.");
1649
1647
  process.exit(1);
1650
1648
  }
1649
+ return {
1650
+ creds,
1651
+ app
1652
+ };
1653
+ }
1654
+ /** A missing name is refused here rather than sent, so the reason reads at the keyboard. */
1655
+ async function detachDomainCommand(hostname, options = {}) {
1656
+ if (!hostname || !hostname.trim()) {
1657
+ console.error("[wawesome] Error: Missing domain. Usage: wawesome domains detach <hostname>");
1658
+ console.error("[wawesome] The domain is typed out because detaching one takes it dark.");
1659
+ process.exit(1);
1660
+ }
1661
+ const { creds, app } = credentialsAndApp(options);
1651
1662
  const name = hostname.trim();
1652
1663
  console.log(`[wawesome] Detaching '${name}' from app '${app}':`);
1653
1664
  console.log("[wawesome] • the domain stops resolving here, so anything pointed at it stops");
@@ -1668,10 +1679,40 @@ async function detachDomainCommand(hostname, options = {}) {
1668
1679
  console.log("[wawesome] or the next deploy attaches the domain again.");
1669
1680
  }
1670
1681
  }
1682
+ /**
1683
+ * What this App answers at, read and printed.
1684
+ *
1685
+ * The one place a Tenant can ask. The records used to exist only in the output
1686
+ * of a deploy that changed something, so a deploy that outran the vendor left
1687
+ * nothing to run but a second deploy, which is refused for changing nothing
1688
+ * (#617). Open to a deploy credential carrying `deploy`, since a pipeline asks
1689
+ * this too.
1690
+ */
1691
+ async function showDomainCommand(options = {}) {
1692
+ const { creds, app } = credentialsAndApp(options);
1693
+ let held;
1694
+ try {
1695
+ held = await listDomains(creds, app);
1696
+ } catch (err) {
1697
+ console.error(`[wawesome] Error: ${whatWentWrong(err)}`);
1698
+ process.exit(1);
1699
+ }
1700
+ if (held.length === 0) {
1701
+ console.log(`[wawesome] No domain is attached to app '${app}'.`);
1702
+ console.log("[wawesome] Declare one in wawesome-function.json and deploy:");
1703
+ console.log(`[wawesome] \x1b[36m${DOMAIN_CONFIG_LINE}\x1b[0m`);
1704
+ console.log("[wawesome] The deploy attaches it and prints the records to add.");
1705
+ return;
1706
+ }
1707
+ console.log(`[wawesome] The domain on app '${app}':`);
1708
+ for (const domain of held) for (const line of attachedDomainLines(domain, "domains")) console.log(line);
1709
+ }
1671
1710
  async function domainsCommand(action, target, options = {}) {
1672
- if ((action || "").toLowerCase().trim() === "detach") return detachDomainCommand(target, options);
1711
+ const verb = (action || "").toLowerCase().trim();
1712
+ if (verb === "detach") return detachDomainCommand(target, options);
1713
+ if (!verb) return showDomainCommand(options);
1673
1714
  console.error(`[wawesome] Error: Unknown command '${["domains", action].filter(Boolean).join(" ")}'.`);
1674
- console.error("[wawesome] Usage: wawesome domains detach <hostname>");
1715
+ console.error("[wawesome] Usage: wawesome domains [detach <hostname>]");
1675
1716
  console.error("[wawesome] A domain is attached by declaring it in wawesome-function.json.");
1676
1717
  process.exit(1);
1677
1718
  }
@@ -1697,10 +1738,22 @@ function whereItGotTo(domain) {
1697
1738
  if (domain.certificate_record_pending) return "not verified yet, and the TXT record is not ready";
1698
1739
  return "not verified yet, so add the TXT record below";
1699
1740
  }
1700
- function whatItIsFor(record) {
1701
- return record.purpose === "certificate" ? "Proves you own the name, and issues the certificate" : "Points traffic here, once the certificate is issued";
1741
+ /**
1742
+ * What a record is for, and when to add it.
1743
+ *
1744
+ * The order is stated rather than implied. The TXT proves control and issues
1745
+ * the certificate; the CNAME is pointed here afterwards, so a name already
1746
+ * serving a live site never answers on a certificate that is not there yet.
1747
+ * That is why the platform validates before the cutover, and a reader holding
1748
+ * the CNAME alone could read the old caption as a label rather than as an
1749
+ * instruction to wait (#617).
1750
+ */
1751
+ function whatItIsFor(purpose) {
1752
+ return purpose === "certificate" ? "Add this first. It proves you own the name and issues the certificate" : "Add this once the certificate is active. It points traffic here";
1702
1753
  }
1703
1754
  const VALUE_COLUMN = " ".repeat(14);
1755
+ /** What the dashboard's empty state shows. Kept the same by hand, not by anything. */
1756
+ const DOMAIN_CONFIG_LINE = "\"domain\": \"shop.client.com\"";
1704
1757
  /**
1705
1758
  * The block a deploy prints for the domain its configuration declares, in the
1706
1759
  * columns the visibility and URL lines above it already use.
@@ -1730,7 +1783,18 @@ function domainLines(outcome) {
1730
1783
  ...outcome.held ? [`${VALUE_COLUMN}Nothing was detached. Take '${outcome.held}' off first with`, `${VALUE_COLUMN} \x1b[36mwawesome domains detach ${outcome.held}\x1b[0m`] : [],
1731
1784
  `${VALUE_COLUMN}The deploy landed, and this App's other address still answers.`
1732
1785
  ];
1733
- const { hostname, records, state, last_checked_at, detaching, certificate_record_pending } = outcome.domain;
1786
+ return attachedDomainLines(outcome.domain, "deploy");
1787
+ }
1788
+ /**
1789
+ * The block for a domain the App carries: where it got to, when the platform
1790
+ * last looked, and the records to add.
1791
+ *
1792
+ * The deploy and `wawesome domains` print this same block. One renderer rather
1793
+ * than two, because two of them drift, and a record going missing from one is
1794
+ * the whole of #610 and #617.
1795
+ */
1796
+ function attachedDomainLines(domain, printedBy = "deploy") {
1797
+ const { hostname, records, state, last_checked_at, detaching, certificate_record_pending } = domain;
1734
1798
  if (detaching) return [
1735
1799
  "",
1736
1800
  ` Domain: \x1b[33m${hostname}: being detached\x1b[0m`,
@@ -1738,20 +1802,36 @@ function domainLines(outcome) {
1738
1802
  `${VALUE_COLUMN}Remove 'domain' from wawesome-function.json, or deploy again`,
1739
1803
  `${VALUE_COLUMN}once it is gone to attach it afresh.`
1740
1804
  ];
1741
- const lines = ["", ` Domain: \x1b[36m${hostname}\x1b[0m: ${whereItGotTo(outcome.domain)}`];
1805
+ const lines = ["", ` Domain: \x1b[36m${hostname}\x1b[0m: ${whereItGotTo(domain)}`];
1806
+ if (last_checked_at) lines.push(`${VALUE_COLUMN}Last checked ${asUtc$1(last_checked_at)}.`);
1742
1807
  if (state.serving) {
1743
- lines.push(`${VALUE_COLUMN}The address above answers too, so nothing pointed at it breaks.`);
1808
+ lines.push(`${VALUE_COLUMN}This app's own address answers too, so nothing pointed at it breaks.`);
1744
1809
  return lines;
1745
1810
  }
1746
- if (last_checked_at) lines.push(`${VALUE_COLUMN}Last checked ${asUtc$1(last_checked_at)}.`);
1747
1811
  const nameWidth = Math.max(...records.map((record) => record.name.length));
1812
+ const row = (type, name, value = "") => ` ${type.padEnd(5)} ${name.padEnd(nameWidth)} ${value}`.trimEnd();
1748
1813
  lines.push("", " DNS records to add at your registrar:");
1749
- for (const record of records) lines.push("", ` ${whatItIsFor(record)}:`, ` ${record.type.padEnd(5)} ${record.name.padEnd(nameWidth)} ${record.value}`);
1750
- lines.push("");
1751
- if (certificate_record_pending) lines.push(`${VALUE_COLUMN}The TXT record that proves you own the name is not ready`, `${VALUE_COLUMN}yet. Deploy again in a minute and it prints here. The`, `${VALUE_COLUMN}deploy is finished either way.`);
1752
- else lines.push(`${VALUE_COLUMN}The deploy is finished. This domain comes up on its own`, `${VALUE_COLUMN}once the records resolve.`);
1814
+ if (certificate_record_pending) lines.push("", ` ${whatItIsFor("certificate")}:`, row("TXT", "not ready yet"));
1815
+ for (const record of records) lines.push("", ` ${whatItIsFor(record.purpose)}:`, row(record.type, record.name, record.value));
1816
+ lines.push("", ...closingLines(certificate_record_pending, printedBy));
1753
1817
  return lines;
1754
1818
  }
1819
+ /**
1820
+ * What is left to do, which is nothing.
1821
+ *
1822
+ * The pending case pointed at another deploy until #617: a deploy that changed
1823
+ * nothing is refused, so the one piece of advice the reader was given was the
1824
+ * one they could not take.
1825
+ */
1826
+ function closingLines(certificateRecordPending, printedBy) {
1827
+ if (!certificateRecordPending) return indented(`${printedBy === "deploy" ? "The deploy is finished. " : ""}This domain comes up on its own once the records resolve.`);
1828
+ const asking = "The TXT record is not ready yet. The platform asks the certificate vendor again on its own.";
1829
+ if (printedBy === "domains") return indented(`${asking} Run this again in a minute.`);
1830
+ return [...indented(`${asking} The deploy is finished either way. To read the record when it lands, run`), `${VALUE_COLUMN} \x1b[36mwawesome domains\x1b[0m`];
1831
+ }
1832
+ function indented(said) {
1833
+ return wrapped(said).map((line) => `${VALUE_COLUMN}${line}`);
1834
+ }
1755
1835
  /** The platform's own prose, kept inside the block it is printed in. */
1756
1836
  function wrapped(said) {
1757
1837
  const width = 64;
@@ -5299,7 +5379,7 @@ cli.command("env [action] [key] [value]", "Manage environment variables (set, li
5299
5379
  cli.command("env set <key> <value>", "Set or overwrite an environment variable on the current app").option("-s, --secret", "Flag variable as secret (write-only)").option("-v, --verbose", "Enable verbose debug output").action((key, value, options) => setEnvVar(key, value, options));
5300
5380
  cli.command("env list", "List environment variables for the current app").alias("env ls").option("-v, --verbose", "Enable verbose debug output").action((options) => listEnvVars(options));
5301
5381
  cli.command("env rm <key>", "Delete an environment variable from the current app").alias("env remove").alias("env delete").alias("env unset").option("-v, --verbose", "Enable verbose debug output").action((key, options) => removeEnvVar(key, options));
5302
- cli.command("domains [action] [hostname]", "Manage the custom domain on the current app (detach)").usage("domains <action> [hostname]\n\nActions:\n detach <hostname> Stop serving the app at a domain you attached\n\nA domain is attached by declaring it in wawesome-function.json and deploying.").example("wawesome domains detach shop.client.com").option("-a, --app <slug>", "App to act on (default: the app in wawesome-function.json)").action((action, hostname, options) => domainsCommand(action, hostname, options));
5382
+ cli.command("domains [action] [hostname]", "Show the custom domain on the current app, its state and its DNS records").usage("domains [action] [hostname]\n\nWith no action, prints the app's domain, where it got to, when the platform last checked, and the records to add.\n\nActions:\n detach <hostname> Stop serving the app at a domain you attached\n\nA domain is attached by declaring it in wawesome-function.json and deploying.").example("wawesome domains").example("wawesome domains detach shop.client.com").option("-a, --app <slug>", "App to act on (default: the app in wawesome-function.json)").action((action, hostname, options) => domainsCommand(action, hostname, options));
5303
5383
  cli.command("domains detach <hostname>", "Stop serving the current app at a domain you attached").option("-a, --app <slug>", "App to act on (default: the app in wawesome-function.json)").action((hostname, options) => detachDomainCommand(hostname, options));
5304
5384
  cli.command("login", "Authenticate with the wawesome.io platform").option("--api <url>", "API URL (default: https://api.wawesome.io)").option("--gateway <url>", "Alias for --api <url>").option("--dashboard <url>", "Dashboard URL (default: https://dashboard.wawesome.io)").option("--provider <name>", "OAuth provider (default: github)").option("--workspace <name>", "Name for the workspace, when signing up without a terminal to prompt").option("--accept-terms", "Accept the terms of service and privacy policy, when signing up without a terminal to prompt").option("-v, --verbose", "Enable verbose debug output").action((options) => login(options));
5305
5385
  cli.command("logout", "Clear stored authentication credentials").action(() => logout());
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wawesome",
3
- "version": "0.12.0",
3
+ "version": "0.13.0",
4
4
  "description": "CLI tool for building and deploying serverless functions on wawesome.io platform",
5
5
  "type": "module",
6
6
  "bin": {