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.
Files changed (3) hide show
  1. package/README.md +35 -12
  2. package/dist/index.mjs +55 -29
  3. 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 files, environment contract and outbound providers | nothing |
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. A member's session calling the
509
- endpoint gets the same answer for the role they hold.
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 two DNS records to add at your registrar. The TXT record proves you
794
- own the name, which is what issues the certificate. The CNAME points traffic here, and you add that
795
- one once the certificate is issued, so a site that is already live never spends a minute answering on
796
- a certificate that is not there yet.
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. The TXT record is minted a moment after the name is claimed, and an attach that
811
- outran it prints a row saying so rather than dropping the record from the list. That row is what this
812
- command fills in, and it is where the deploy sends you: deploying again would change nothing, and a
813
- deploy that changes nothing is refused.
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.16.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, because the two are
1877
- * added a day apart: the TXT issues the certificate, and the CNAME is pointed
1878
- * here afterwards so a site that is already live never answers on a
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 "ownership verified, and the certificate is being issued";
1889
- if (domain.certificate_record_pending) return "not verified yet, and the TXT record is not ready";
1890
- return "not verified yet, so add the TXT record below";
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 control and issues
1896
- * the certificate; the CNAME is pointed here afterwards, so a name already
1897
- * serving a live site never answers on a certificate that is not there yet.
1898
- * That is why the platform validates before the cutover, and a reader holding
1899
- * the CNAME alone could read the old caption as a label rather than as an
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
- 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";
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, certificate_record_pending, " "));
1969
- lines.push("", ...closingLines(certificate_record_pending, printedBy));
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.certificate_record_pending, " "));
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, certificateRecordPending, indent) {
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 (certificateRecordPending) lines.push("", `${indent}${whatItIsFor("certificate")}:`, row("TXT", "not ready yet"));
2017
- for (const record of records) lines.push("", `${indent}${whatItIsFor(record.purpose)}:`, row(record.type, record.name, record.value));
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(certificateRecordPending, printedBy) {
2033
- if (!certificateRecordPending) return indented(`${printedBy === "deploy" ? "The deploy is finished. " : ""}This domain comes up on its own once the records resolve.`);
2034
- const asking = "The TXT record is not ready yet. The platform asks the certificate vendor again on its own.";
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);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wawesome",
3
- "version": "0.16.0",
3
+ "version": "0.17.0",
4
4
  "description": "CLI tool for building and deploying serverless functions on wawesome.io platform",
5
5
  "type": "module",
6
6
  "bin": {