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.
- package/README.md +15 -0
- package/dist/index.mjs +101 -21
- 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.
|
|
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
|
-
/**
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1701
|
-
|
|
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
|
-
|
|
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(
|
|
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}
|
|
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
|
-
|
|
1750
|
-
lines.push("");
|
|
1751
|
-
|
|
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]", "
|
|
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());
|