wawesome 0.16.0 → 0.17.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 +35 -12
- package/dist/index.mjs +55 -29
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -478,9 +478,9 @@ concluding the platform cannot do it.
|
|
|
478
478
|
|
|
479
479
|
| Tool | What it does | Needs |
|
|
480
480
|
|:---------------------------|:------------------------------------------------------------------------|:--------------|
|
|
481
|
-
| `whoami` | The workspace, where it is administered, the words this credential carries, the Apps it reaches | nothing |
|
|
481
|
+
| `whoami` | The workspace, where it is administered, the words this credential carries and which of them a tool here asks for, the Apps it reaches | nothing |
|
|
482
482
|
| `list_templates` | The template catalogue, with what each one is for | nothing |
|
|
483
|
-
| `get_template` | One template's
|
|
483
|
+
| `get_template` | One template's source, environment contract and outbound providers; its tests and build configuration are named, and read on request | nothing |
|
|
484
484
|
| `list_apps` | The Apps the credential reaches, each with its address and its dashboard page | `read:apps` |
|
|
485
485
|
| `list_functions` | The Functions in one App, and what their live version carries | `read:functions` |
|
|
486
486
|
| `list_versions` | A Function's Versions, and which one is live | `read:functions` |
|
|
@@ -493,6 +493,8 @@ concluding the platform cannot do it.
|
|
|
493
493
|
| `deploy_function` | Deploys code and the static files it carries | `write:functions` |
|
|
494
494
|
| `deploy_status` | How a deploy went, by the handle it answered with | `read:functions` |
|
|
495
495
|
| `rollback_function` | Puts a Version the Function already has back on its public address | `write:functions` |
|
|
496
|
+
| `request_upload` | Asks the person you are working with for files the conversation cannot produce, and answers a link to give them | `write:functions` |
|
|
497
|
+
| `list_uploads` | What the person sent against one upload session, ready to declare on the next deploy | `read:functions` |
|
|
496
498
|
| `set_env_var` | Sets an App's environment variable, read at the next invocation | `write:env` |
|
|
497
499
|
| `attach_domain` | Attaches a domain to an App and answers the DNS records that make it work | `write:domains` |
|
|
498
500
|
| `cancel_domain_claim` | Gives up a domain claim nobody has proved control of, freeing the App's place | `write:domains` |
|
|
@@ -505,8 +507,11 @@ beside it, and the presets below are named in exactly these words.
|
|
|
505
507
|
|
|
506
508
|
`whoami` is the one to call first. It answers the workspace, every word the credential carries and
|
|
507
509
|
the Apps it reaches, and it asks for nothing — so your agent can say what it is able to do before it
|
|
508
|
-
offers, rather than finding the edge by being refused in front of you.
|
|
509
|
-
|
|
510
|
+
offers, rather than finding the edge by being refused in front of you. Each word also says whether a
|
|
511
|
+
tool here asks for it. Your credential can carry `write:schedules` with no schedule tool on this
|
|
512
|
+
endpoint, and the mark is what stops an agent offering to schedule a job: a word with no tool behind
|
|
513
|
+
it is done on the dashboard rather than being withheld from you. A member's session calling the endpoint gets
|
|
514
|
+
the same answer for the role they hold.
|
|
510
515
|
|
|
511
516
|
A deploy is two calls. The first says which of your files the platform does not already hold, by
|
|
512
517
|
content hash; your agent uploads those bytes to `PUT /v1/assets/{content_hash}` with the same
|
|
@@ -681,6 +686,18 @@ Two rules to know about:
|
|
|
681
686
|
`favicon.ico`, `robots.txt` and a `.well-known/` directory are what that is for; hashed build
|
|
682
687
|
output belongs under `assets/`.
|
|
683
688
|
|
|
689
|
+
**A version carries exactly the files its deploy declared.** Nothing is inherited from the version
|
|
690
|
+
before it, so taking `"assets"` out of `wawesome-function.json` — or pointing it at a directory your
|
|
691
|
+
build no longer writes to — is not an edit but every file on the site going at once. A deploy that
|
|
692
|
+
would do that against a Function serving files today is refused, and tells you how many would go:
|
|
693
|
+
|
|
694
|
+
```bash
|
|
695
|
+
npx wawesome deploy --drop-assets
|
|
696
|
+
```
|
|
697
|
+
|
|
698
|
+
Removing *some* of the files is ordinary editing and needs nothing: only the wholesale drop is
|
|
699
|
+
refused, because that is the shape of a mistake rather than of a decision.
|
|
700
|
+
|
|
684
701
|
**A page is a file like any other.** An `index.html` your build wrote is deployed, hashed, retained
|
|
685
702
|
and billed exactly as your other files are, and it is what a directory-style address resolves to: a
|
|
686
703
|
path whose last segment carries no extension is served the `index.html` beneath it, so `/`, `/about`,
|
|
@@ -790,10 +807,16 @@ Add `"domain"` and your App answers at a name you own, beside the address it alr
|
|
|
790
807
|
}
|
|
791
808
|
```
|
|
792
809
|
|
|
793
|
-
The deploy attaches it and prints
|
|
794
|
-
own the name
|
|
795
|
-
|
|
796
|
-
|
|
810
|
+
The deploy attaches it and prints three DNS records to add at your registrar. The TXT record proves
|
|
811
|
+
you own the name. The `_acme-challenge` CNAME beside it issues the certificate and keeps renewing it
|
|
812
|
+
for as long as it stays there, so nobody comes back to your registrar at renewal time — if an
|
|
813
|
+
`_acme-challenge` TXT record is already at that name, delete it, because a TXT and this CNAME cannot
|
|
814
|
+
both sit there. The last CNAME points traffic here, and you add that one once the certificate is
|
|
815
|
+
issued, so a site that is already live never spends a minute answering on a certificate that is not
|
|
816
|
+
there yet.
|
|
817
|
+
|
|
818
|
+
Nothing you are asked to add ever changes. Copy a value into your registrar, come back an hour later,
|
|
819
|
+
and it is the same value.
|
|
797
820
|
|
|
798
821
|
Your deploy waits for neither. It attaches the domain, prints the records and finishes, and every
|
|
799
822
|
later deploy states where the domain got to: not verified, ownership verified, certificate issued, or
|
|
@@ -807,10 +830,10 @@ npx wawesome domains
|
|
|
807
830
|
|
|
808
831
|
It prints the domain, where it got to, when the platform last checked, and the records to add. It
|
|
809
832
|
changes nothing, and a deploy credential carrying `read:domains` can run it, so a pipeline reads the
|
|
810
|
-
same thing you do.
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
833
|
+
same thing you do. Where the platform does not hold a record yet it prints a row saying so rather
|
|
834
|
+
than dropping the record from the list. That row is what this command fills in, and it is where the
|
|
835
|
+
deploy sends you: deploying again would change nothing, and a deploy that changes nothing is
|
|
836
|
+
refused.
|
|
814
837
|
|
|
815
838
|
A custom domain is granted from the Solo plan upwards, one per App. On a plan that grants none, the
|
|
816
839
|
deploy prints the refusal and lands everything else it was doing.
|
package/dist/index.mjs
CHANGED
|
@@ -905,7 +905,7 @@ async function buildHandler(entryInput, config, options) {
|
|
|
905
905
|
* that has to name this version — `--version`, the dependency a scaffolded
|
|
906
906
|
* project pins — reads it here, so a release bumps one file.
|
|
907
907
|
*/
|
|
908
|
-
const CLI_VERSION = "0.
|
|
908
|
+
const CLI_VERSION = "0.17.0";
|
|
909
909
|
//#endregion
|
|
910
910
|
//#region src/prompt.ts
|
|
911
911
|
/**
|
|
@@ -1873,10 +1873,9 @@ function domainsUrl(creds, app) {
|
|
|
1873
1873
|
/**
|
|
1874
1874
|
* Where a domain got to, said beside the name itself.
|
|
1875
1875
|
*
|
|
1876
|
-
* Each state names the one record that is outstanding
|
|
1877
|
-
*
|
|
1878
|
-
*
|
|
1879
|
-
* certificate that is not there yet.
|
|
1876
|
+
* Each state names the one record that is outstanding. The two that prove the
|
|
1877
|
+
* name go in first and the traffic CNAME a day later, so a site that is already
|
|
1878
|
+
* live never answers on a certificate that is not there yet.
|
|
1880
1879
|
*
|
|
1881
1880
|
* Read off the records too, never the states alone: naming a record the payload
|
|
1882
1881
|
* does not carry left a reader holding the CNAME alone (#610).
|
|
@@ -1885,22 +1884,26 @@ function whereItGotTo(domain) {
|
|
|
1885
1884
|
const { state } = domain;
|
|
1886
1885
|
if (state.serving) return "serving";
|
|
1887
1886
|
if (state.certificate_active) return "certificate issued, so point the CNAME here to cut over";
|
|
1888
|
-
if (state.ownership_verified) return "
|
|
1889
|
-
if (domain.
|
|
1890
|
-
return "not verified yet, so add the
|
|
1887
|
+
if (state.ownership_verified) return domain.certificate_record_pending ? "name verified, and the certificate record is not ready" : "name verified, so add the certificate CNAME below";
|
|
1888
|
+
if (domain.ownership_record_pending) return "not verified yet, and the TXT record is not ready";
|
|
1889
|
+
return "not verified yet, so add the records below";
|
|
1891
1890
|
}
|
|
1892
1891
|
/**
|
|
1893
1892
|
* What a record is for, and when to add it.
|
|
1894
1893
|
*
|
|
1895
|
-
* The order is stated rather than implied. The TXT proves
|
|
1896
|
-
*
|
|
1897
|
-
* serving a live site never answers on a
|
|
1898
|
-
* That is why the platform validates before
|
|
1899
|
-
* the
|
|
1900
|
-
* instruction to wait (#617).
|
|
1894
|
+
* The order is stated rather than implied. The TXT proves the name is theirs
|
|
1895
|
+
* and the CNAME beside it issues the certificate; the traffic record is pointed
|
|
1896
|
+
* here afterwards, so a name already serving a live site never answers on a
|
|
1897
|
+
* certificate that is not there yet. That is why the platform validates before
|
|
1898
|
+
* the cutover, and a reader holding the traffic record alone could read the old
|
|
1899
|
+
* caption as a label rather than as an instruction to wait (#617).
|
|
1901
1900
|
*/
|
|
1902
1901
|
function whatItIsFor(purpose) {
|
|
1903
|
-
|
|
1902
|
+
switch (purpose) {
|
|
1903
|
+
case "ownership": return "Add this first. It proves you own the name";
|
|
1904
|
+
case "certificate": return "Add this first too. It issues the certificate and keeps it renewed";
|
|
1905
|
+
default: return "Add this once the certificate is active. It points traffic here";
|
|
1906
|
+
}
|
|
1904
1907
|
}
|
|
1905
1908
|
/** What the dashboard's empty state shows. Kept the same by hand, not by anything. */
|
|
1906
1909
|
const DOMAIN_CONFIG_LINE = "\"domain\": \"shop.client.com\"";
|
|
@@ -1948,7 +1951,7 @@ function domainLines(outcome) {
|
|
|
1948
1951
|
* the whole of #610 and #617.
|
|
1949
1952
|
*/
|
|
1950
1953
|
function attachedDomainLines(domain, printedBy = "deploy") {
|
|
1951
|
-
const { hostname, records, state, last_checked_at, detaching, certificate_record_pending } = domain;
|
|
1954
|
+
const { hostname, records, state, last_checked_at, detaching, ownership_record_pending, certificate_record_pending } = domain;
|
|
1952
1955
|
if (detaching) return [
|
|
1953
1956
|
"",
|
|
1954
1957
|
` Domain: \x1b[33m${hostname}: being detached\x1b[0m`,
|
|
@@ -1964,9 +1967,13 @@ function attachedDomainLines(domain, printedBy = "deploy") {
|
|
|
1964
1967
|
lines.push(`${VALUE_COLUMN}This app's own address answers too, so nothing pointed at it breaks.`);
|
|
1965
1968
|
return lines;
|
|
1966
1969
|
}
|
|
1970
|
+
const pending = Boolean(ownership_record_pending || certificate_record_pending);
|
|
1967
1971
|
lines.push("", " DNS records to add at your registrar:");
|
|
1968
|
-
lines.push(...recordLines(records,
|
|
1969
|
-
|
|
1972
|
+
lines.push(...recordLines(records, {
|
|
1973
|
+
ownership_record_pending,
|
|
1974
|
+
certificate_record_pending
|
|
1975
|
+
}, " "));
|
|
1976
|
+
lines.push("", ...closingLines(pending, printedBy));
|
|
1970
1977
|
return lines;
|
|
1971
1978
|
}
|
|
1972
1979
|
/**
|
|
@@ -1993,9 +2000,9 @@ function pairLines(domain, legs, printedBy) {
|
|
|
1993
2000
|
lines.push("", " DNS records to add at your registrar:");
|
|
1994
2001
|
for (const leg of outstanding) {
|
|
1995
2002
|
lines.push("", ` ${leg.hostname}:`);
|
|
1996
|
-
lines.push(...recordLines(leg.records ?? [], leg
|
|
2003
|
+
lines.push(...recordLines(leg.records ?? [], leg, " "));
|
|
1997
2004
|
}
|
|
1998
|
-
const pending = outstanding.some((leg) => leg.certificate_record_pending);
|
|
2005
|
+
const pending = outstanding.some((leg) => leg.ownership_record_pending || leg.certificate_record_pending);
|
|
1999
2006
|
lines.push("", ...closingLines(pending, printedBy));
|
|
2000
2007
|
return lines;
|
|
2001
2008
|
}
|
|
@@ -2009,12 +2016,16 @@ function whereTheLegGotTo(leg) {
|
|
|
2009
2016
|
* 40-odd characters a person has to copy exactly, and a fourth column puts the
|
|
2010
2017
|
* row past the width of the terminal it is being copied out of.
|
|
2011
2018
|
*/
|
|
2012
|
-
function recordLines(records,
|
|
2019
|
+
function recordLines(records, pending, indent) {
|
|
2013
2020
|
const nameWidth = Math.max(0, ...records.map((record) => record.name.length));
|
|
2014
2021
|
const row = (type, name, value = "") => `${indent} ${type.padEnd(5)} ${name.padEnd(nameWidth)} ${value}`.trimEnd();
|
|
2015
2022
|
const lines = [];
|
|
2016
|
-
if (
|
|
2017
|
-
|
|
2023
|
+
if (pending.ownership_record_pending) lines.push("", `${indent}${whatItIsFor("ownership")}:`, row("TXT", "not ready yet"));
|
|
2024
|
+
if (pending.certificate_record_pending) lines.push("", `${indent}${whatItIsFor("certificate")}:`, row("CNAME", "not ready yet"));
|
|
2025
|
+
for (const record of records) {
|
|
2026
|
+
lines.push("", `${indent}${whatItIsFor(record.purpose)}:`, row(record.type, record.name, record.value));
|
|
2027
|
+
if (record.purpose === "certificate") lines.push(`${indent} Delete any _acme-challenge TXT record already at that name.`);
|
|
2028
|
+
}
|
|
2018
2029
|
return lines;
|
|
2019
2030
|
}
|
|
2020
2031
|
/** Every hostname a claim answers at, for a block naming the claim in one line. */
|
|
@@ -2029,9 +2040,9 @@ function claimedHostnames(domain) {
|
|
|
2029
2040
|
* nothing is refused, so the one piece of advice the reader was given was the
|
|
2030
2041
|
* one they could not take.
|
|
2031
2042
|
*/
|
|
2032
|
-
function closingLines(
|
|
2033
|
-
if (!
|
|
2034
|
-
const asking = "
|
|
2043
|
+
function closingLines(aRecordIsPending, printedBy) {
|
|
2044
|
+
if (!aRecordIsPending) return indented(`${printedBy === "deploy" ? "The deploy is finished. " : ""}This domain comes up on its own once the records resolve.`);
|
|
2045
|
+
const asking = "A record is not ready yet. The platform asks the certificate vendor again on its own.";
|
|
2035
2046
|
if (printedBy === "domains") return indented(`${asking} Run this again in a minute.`);
|
|
2036
2047
|
return [...indented(`${asking} The deploy is finished either way. To read the record when it lands, run`), `${VALUE_COLUMN} \x1b[36mwawesome domains\x1b[0m`];
|
|
2037
2048
|
}
|
|
@@ -2968,7 +2979,8 @@ function shadowingLines(shadowed) {
|
|
|
2968
2979
|
function declaredFields(declared) {
|
|
2969
2980
|
return {
|
|
2970
2981
|
...declared.visibility ? { visibility: declared.visibility } : {},
|
|
2971
|
-
...declared.confirmPublish ? { confirm_publish: true } : {}
|
|
2982
|
+
...declared.confirmPublish ? { confirm_publish: true } : {},
|
|
2983
|
+
...declared.confirmAssetDrop ? { confirm_asset_drop: true } : {}
|
|
2972
2984
|
};
|
|
2973
2985
|
}
|
|
2974
2986
|
/**
|
|
@@ -2982,6 +2994,15 @@ function declaredVisibility(value) {
|
|
|
2982
2994
|
console.error("[wawesome] It must be \"public\" or \"private\".");
|
|
2983
2995
|
process.exit(1);
|
|
2984
2996
|
}
|
|
2997
|
+
function reportAssetDropRefusal(message) {
|
|
2998
|
+
console.error(`\n[wawesome] \x1b[31mError: ${message}\x1b[0m`);
|
|
2999
|
+
console.error("[wawesome] Nothing was deployed. A version carries exactly the files its");
|
|
3000
|
+
console.error("[wawesome] deploy declared, so this one would take every file the live");
|
|
3001
|
+
console.error("[wawesome] version serves off the site at once.");
|
|
3002
|
+
console.error("[wawesome] Point \x1B[36m\"assets\"\x1B[0m in wawesome-function.json at the directory");
|
|
3003
|
+
console.error("[wawesome] holding them, or re-run with \x1B[36mwawesome deploy --drop-assets\x1B[0m to");
|
|
3004
|
+
console.error("[wawesome] remove them.\n");
|
|
3005
|
+
}
|
|
2985
3006
|
function reportPublishRefusal(message) {
|
|
2986
3007
|
console.error(`\n[wawesome] \x1b[31mError: ${message}\x1b[0m`);
|
|
2987
3008
|
console.error("[wawesome] Nothing was deployed. This Function is private, so its address");
|
|
@@ -3009,7 +3030,8 @@ async function deploy(entryInput, options) {
|
|
|
3009
3030
|
const { app, function: funcName } = config;
|
|
3010
3031
|
const declared = {
|
|
3011
3032
|
visibility: declaredVisibility(config.visibility),
|
|
3012
|
-
confirmPublish: Boolean(options.publish)
|
|
3033
|
+
confirmPublish: Boolean(options.publish),
|
|
3034
|
+
confirmAssetDrop: Boolean(options.dropAssets)
|
|
3013
3035
|
};
|
|
3014
3036
|
const domainName = declaredDomain(config.domain);
|
|
3015
3037
|
if (isVerbose) {
|
|
@@ -3100,6 +3122,10 @@ async function deploy(entryInput, options) {
|
|
|
3100
3122
|
reportPublishRefusal(refusal.message);
|
|
3101
3123
|
process.exit(1);
|
|
3102
3124
|
}
|
|
3125
|
+
if (refusal.reason === "asset-drop-needs-confirmation") {
|
|
3126
|
+
reportAssetDropRefusal(refusal.message);
|
|
3127
|
+
process.exit(1);
|
|
3128
|
+
}
|
|
3103
3129
|
console.error(`\n[wawesome] \x1b[31mError: ${refusal.message}\x1b[0m`);
|
|
3104
3130
|
const advice = planLimitAdvice(refusal.reason);
|
|
3105
3131
|
if (advice) console.error(`[wawesome] ${advice}\n`);
|
|
@@ -5920,7 +5946,7 @@ async function credentialsCommand(action, target, options) {
|
|
|
5920
5946
|
//#region src/index.ts
|
|
5921
5947
|
const cli = cac("wawesome");
|
|
5922
5948
|
cli.command("build [entry]", "Bundle a serverless function to an optimized JS file").option("-o, --out <path>", "Output JS bundle path", { default: "dist/index.js" }).option("-v, --verbose", "Enable verbose debug output").action((entry, options) => buildJs(entry, options));
|
|
5923
|
-
cli.command("deploy [entry]", "Build, upload, and promote a serverless function").option("-o, --out <path>", "Output JS bundle path", { default: "dist/index.js" }).option("-v, --verbose", "Enable verbose debug output").option("--skip-build", "Skip the build step, deploy an already-built bundle").option("--publish", "Confirm putting a private function back on its public address").action((entry, options) => deploy(entry, options));
|
|
5949
|
+
cli.command("deploy [entry]", "Build, upload, and promote a serverless function").option("-o, --out <path>", "Output JS bundle path", { default: "dist/index.js" }).option("-v, --verbose", "Enable verbose debug output").option("--skip-build", "Skip the build step, deploy an already-built bundle").option("--publish", "Confirm putting a private function back on its public address").option("--drop-assets", "Confirm a deploy carrying no static files where the live version serves some").action((entry, options) => deploy(entry, options));
|
|
5924
5950
|
cli.command("version [action] [target]", "Manage versions (e.g. 'version list', 'version switch [version]')").option("-e, --env <environment>", "Target environment (default: production)").option("-v, --verbose", "Enable verbose debug output").action((action, target, options) => {
|
|
5925
5951
|
if (!action || action === "list" || action === "ls") return listVersions(options);
|
|
5926
5952
|
if (action === "switch" || action === "use" || action === "select") return switchVersion(target, options);
|