github.com/nektos/act@v0.2.63/pkg/runner/testdata/actions/node12/node_modules/node-fetch/README.md (about)

     1  node-fetch
     2  ==========
     3  
     4  [![npm version][npm-image]][npm-url]
     5  [![build status][travis-image]][travis-url]
     6  [![coverage status][codecov-image]][codecov-url]
     7  [![install size][install-size-image]][install-size-url]
     8  [![Discord][discord-image]][discord-url]
     9  
    10  A light-weight module that brings `window.fetch` to Node.js
    11  
    12  (We are looking for [v2 maintainers and collaborators](https://github.com/bitinn/node-fetch/issues/567))
    13  
    14  [![Backers][opencollective-image]][opencollective-url]
    15  
    16  <!-- TOC -->
    17  
    18  - [Motivation](#motivation)
    19  - [Features](#features)
    20  - [Difference from client-side fetch](#difference-from-client-side-fetch)
    21  - [Installation](#installation)
    22  - [Loading and configuring the module](#loading-and-configuring-the-module)
    23  - [Common Usage](#common-usage)
    24      - [Plain text or HTML](#plain-text-or-html)
    25      - [JSON](#json)
    26      - [Simple Post](#simple-post)
    27      - [Post with JSON](#post-with-json)
    28      - [Post with form parameters](#post-with-form-parameters)
    29      - [Handling exceptions](#handling-exceptions)
    30      - [Handling client and server errors](#handling-client-and-server-errors)
    31  - [Advanced Usage](#advanced-usage)
    32      - [Streams](#streams)
    33      - [Buffer](#buffer)
    34      - [Accessing Headers and other Meta data](#accessing-headers-and-other-meta-data)
    35      - [Extract Set-Cookie Header](#extract-set-cookie-header)
    36      - [Post data using a file stream](#post-data-using-a-file-stream)
    37      - [Post with form-data (detect multipart)](#post-with-form-data-detect-multipart)
    38      - [Request cancellation with AbortSignal](#request-cancellation-with-abortsignal)
    39  - [API](#api)
    40      - [fetch(url[, options])](#fetchurl-options)
    41      - [Options](#options)
    42      - [Class: Request](#class-request)
    43      - [Class: Response](#class-response)
    44      - [Class: Headers](#class-headers)
    45      - [Interface: Body](#interface-body)
    46      - [Class: FetchError](#class-fetcherror)
    47  - [License](#license)
    48  - [Acknowledgement](#acknowledgement)
    49  
    50  <!-- /TOC -->
    51  
    52  ## Motivation
    53  
    54  Instead of implementing `XMLHttpRequest` in Node.js to run browser-specific [Fetch polyfill](https://github.com/github/fetch), why not go from native `http` to `fetch` API directly? Hence, `node-fetch`, minimal code for a `window.fetch` compatible API on Node.js runtime.
    55  
    56  See Matt Andrews' [isomorphic-fetch](https://github.com/matthew-andrews/isomorphic-fetch) or Leonardo Quixada's [cross-fetch](https://github.com/lquixada/cross-fetch) for isomorphic usage (exports `node-fetch` for server-side, `whatwg-fetch` for client-side).
    57  
    58  ## Features
    59  
    60  - Stay consistent with `window.fetch` API.
    61  - Make conscious trade-off when following [WHATWG fetch spec][whatwg-fetch] and [stream spec](https://streams.spec.whatwg.org/) implementation details, document known differences.
    62  - Use native promise but allow substituting it with [insert your favorite promise library].
    63  - Use native Node streams for body on both request and response.
    64  - Decode content encoding (gzip/deflate) properly and convert string output (such as `res.text()` and `res.json()`) to UTF-8 automatically.
    65  - Useful extensions such as timeout, redirect limit, response size limit, [explicit errors](ERROR-HANDLING.md) for troubleshooting.
    66  
    67  ## Difference from client-side fetch
    68  
    69  - See [Known Differences](LIMITS.md) for details.
    70  - If you happen to use a missing feature that `window.fetch` offers, feel free to open an issue.
    71  - Pull requests are welcomed too!
    72  
    73  ## Installation
    74  
    75  Current stable release (`2.x`)
    76  
    77  ```sh
    78  $ npm install node-fetch
    79  ```
    80  
    81  ## Loading and configuring the module
    82  We suggest you load the module via `require` until the stabilization of ES modules in node:
    83  ```js
    84  const fetch = require('node-fetch');
    85  ```
    86  
    87  If you are using a Promise library other than native, set it through `fetch.Promise`:
    88  ```js
    89  const Bluebird = require('bluebird');
    90  
    91  fetch.Promise = Bluebird;
    92  ```
    93  
    94  ## Common Usage
    95  
    96  NOTE: The documentation below is up-to-date with `2.x` releases; see the [`1.x` readme](https://github.com/bitinn/node-fetch/blob/1.x/README.md), [changelog](https://github.com/bitinn/node-fetch/blob/1.x/CHANGELOG.md) and [2.x upgrade guide](UPGRADE-GUIDE.md) for the differences.
    97  
    98  #### Plain text or HTML
    99  ```js
   100  fetch('https://github.com/')
   101      .then(res => res.text())
   102      .then(body => console.log(body));
   103  ```
   104  
   105  #### JSON
   106  
   107  ```js
   108  
   109  fetch('https://api.github.com/users/github')
   110      .then(res => res.json())
   111      .then(json => console.log(json));
   112  ```
   113  
   114  #### Simple Post
   115  ```js
   116  fetch('https://httpbin.org/post', { method: 'POST', body: 'a=1' })
   117      .then(res => res.json()) // expecting a json response
   118      .then(json => console.log(json));
   119  ```
   120  
   121  #### Post with JSON
   122  
   123  ```js
   124  const body = { a: 1 };
   125  
   126  fetch('https://httpbin.org/post', {
   127          method: 'post',
   128          body:    JSON.stringify(body),
   129          headers: { 'Content-Type': 'application/json' },
   130      })
   131      .then(res => res.json())
   132      .then(json => console.log(json));
   133  ```
   134  
   135  #### Post with form parameters
   136  `URLSearchParams` is available in Node.js as of v7.5.0. See [official documentation](https://nodejs.org/api/url.html#url_class_urlsearchparams) for more usage methods.
   137  
   138  NOTE: The `Content-Type` header is only set automatically to `x-www-form-urlencoded` when an instance of `URLSearchParams` is given as such:
   139  
   140  ```js
   141  const { URLSearchParams } = require('url');
   142  
   143  const params = new URLSearchParams();
   144  params.append('a', 1);
   145  
   146  fetch('https://httpbin.org/post', { method: 'POST', body: params })
   147      .then(res => res.json())
   148      .then(json => console.log(json));
   149  ```
   150  
   151  #### Handling exceptions
   152  NOTE: 3xx-5xx responses are *NOT* exceptions and should be handled in `then()`; see the next section for more information.
   153  
   154  Adding a catch to the fetch promise chain will catch *all* exceptions, such as errors originating from node core libraries, network errors and operational errors, which are instances of FetchError. See the [error handling document](ERROR-HANDLING.md)  for more details.
   155  
   156  ```js
   157  fetch('https://domain.invalid/')
   158      .catch(err => console.error(err));
   159  ```
   160  
   161  #### Handling client and server errors
   162  It is common to create a helper function to check that the response contains no client (4xx) or server (5xx) error responses:
   163  
   164  ```js
   165  function checkStatus(res) {
   166      if (res.ok) { // res.status >= 200 && res.status < 300
   167          return res;
   168      } else {
   169          throw MyCustomError(res.statusText);
   170      }
   171  }
   172  
   173  fetch('https://httpbin.org/status/400')
   174      .then(checkStatus)
   175      .then(res => console.log('will not get here...'))
   176  ```
   177  
   178  ## Advanced Usage
   179  
   180  #### Streams
   181  The "Node.js way" is to use streams when possible:
   182  
   183  ```js
   184  fetch('https://assets-cdn.github.com/images/modules/logos_page/Octocat.png')
   185      .then(res => {
   186          const dest = fs.createWriteStream('./octocat.png');
   187          res.body.pipe(dest);
   188      });
   189  ```
   190  
   191  In Node.js 14 you can also use async iterators to read `body`; however, be careful to catch
   192  errors -- the longer a response runs, the more likely it is to encounter an error.
   193  
   194  ```js
   195  const fetch = require('node-fetch');
   196  const response = await fetch('https://httpbin.org/stream/3');
   197  try {
   198  	for await (const chunk of response.body) {
   199  		console.dir(JSON.parse(chunk.toString()));
   200  	}
   201  } catch (err) {
   202  	console.error(err.stack);
   203  }
   204  ```
   205  
   206  In Node.js 12 you can also use async iterators to read `body`; however, async iterators with streams
   207  did not mature until Node.js 14, so you need to do some extra work to ensure you handle errors
   208  directly from the stream and wait on it response to fully close.
   209  
   210  ```js
   211  const fetch = require('node-fetch');
   212  const read = async body => {
   213      let error;
   214      body.on('error', err => {
   215          error = err;
   216      });
   217      for await (const chunk of body) {
   218          console.dir(JSON.parse(chunk.toString()));
   219      }
   220      return new Promise((resolve, reject) => {
   221          body.on('close', () => {
   222              error ? reject(error) : resolve();
   223          });
   224      });
   225  };
   226  try {
   227      const response = await fetch('https://httpbin.org/stream/3');
   228      await read(response.body);
   229  } catch (err) {
   230      console.error(err.stack);
   231  }
   232  ```
   233  
   234  #### Buffer
   235  If you prefer to cache binary data in full, use buffer(). (NOTE: `buffer()` is a `node-fetch`-only API)
   236  
   237  ```js
   238  const fileType = require('file-type');
   239  
   240  fetch('https://assets-cdn.github.com/images/modules/logos_page/Octocat.png')
   241      .then(res => res.buffer())
   242      .then(buffer => fileType(buffer))
   243      .then(type => { /* ... */ });
   244  ```
   245  
   246  #### Accessing Headers and other Meta data
   247  ```js
   248  fetch('https://github.com/')
   249      .then(res => {
   250          console.log(res.ok);
   251          console.log(res.status);
   252          console.log(res.statusText);
   253          console.log(res.headers.raw());
   254          console.log(res.headers.get('content-type'));
   255      });
   256  ```
   257  
   258  #### Extract Set-Cookie Header
   259  
   260  Unlike browsers, you can access raw `Set-Cookie` headers manually using `Headers.raw()`. This is a `node-fetch` only API.
   261  
   262  ```js
   263  fetch(url).then(res => {
   264      // returns an array of values, instead of a string of comma-separated values
   265      console.log(res.headers.raw()['set-cookie']);
   266  });
   267  ```
   268  
   269  #### Post data using a file stream
   270  
   271  ```js
   272  const { createReadStream } = require('fs');
   273  
   274  const stream = createReadStream('input.txt');
   275  
   276  fetch('https://httpbin.org/post', { method: 'POST', body: stream })
   277      .then(res => res.json())
   278      .then(json => console.log(json));
   279  ```
   280  
   281  #### Post with form-data (detect multipart)
   282  
   283  ```js
   284  const FormData = require('form-data');
   285  
   286  const form = new FormData();
   287  form.append('a', 1);
   288  
   289  fetch('https://httpbin.org/post', { method: 'POST', body: form })
   290      .then(res => res.json())
   291      .then(json => console.log(json));
   292  
   293  // OR, using custom headers
   294  // NOTE: getHeaders() is non-standard API
   295  
   296  const form = new FormData();
   297  form.append('a', 1);
   298  
   299  const options = {
   300      method: 'POST',
   301      body: form,
   302      headers: form.getHeaders()
   303  }
   304  
   305  fetch('https://httpbin.org/post', options)
   306      .then(res => res.json())
   307      .then(json => console.log(json));
   308  ```
   309  
   310  #### Request cancellation with AbortSignal
   311  
   312  > NOTE: You may cancel streamed requests only on Node >= v8.0.0
   313  
   314  You may cancel requests with `AbortController`. A suggested implementation is [`abort-controller`](https://www.npmjs.com/package/abort-controller).
   315  
   316  An example of timing out a request after 150ms could be achieved as the following:
   317  
   318  ```js
   319  import AbortController from 'abort-controller';
   320  
   321  const controller = new AbortController();
   322  const timeout = setTimeout(
   323    () => { controller.abort(); },
   324    150,
   325  );
   326  
   327  fetch(url, { signal: controller.signal })
   328    .then(res => res.json())
   329    .then(
   330      data => {
   331        useData(data)
   332      },
   333      err => {
   334        if (err.name === 'AbortError') {
   335          // request was aborted
   336        }
   337      },
   338    )
   339    .finally(() => {
   340      clearTimeout(timeout);
   341    });
   342  ```
   343  
   344  See [test cases](https://github.com/bitinn/node-fetch/blob/master/test/test.js) for more examples.
   345  
   346  
   347  ## API
   348  
   349  ### fetch(url[, options])
   350  
   351  - `url` A string representing the URL for fetching
   352  - `options` [Options](#fetch-options) for the HTTP(S) request
   353  - Returns: <code>Promise&lt;[Response](#class-response)&gt;</code>
   354  
   355  Perform an HTTP(S) fetch.
   356  
   357  `url` should be an absolute url, such as `https://example.com/`. A path-relative URL (`/file/under/root`) or protocol-relative URL (`//can-be-http-or-https.com/`) will result in a rejected `Promise`.
   358  
   359  <a id="fetch-options"></a>
   360  ### Options
   361  
   362  The default values are shown after each option key.
   363  
   364  ```js
   365  {
   366      // These properties are part of the Fetch Standard
   367      method: 'GET',
   368      headers: {},        // request headers. format is the identical to that accepted by the Headers constructor (see below)
   369      body: null,         // request body. can be null, a string, a Buffer, a Blob, or a Node.js Readable stream
   370      redirect: 'follow', // set to `manual` to extract redirect headers, `error` to reject redirect
   371      signal: null,       // pass an instance of AbortSignal to optionally abort requests
   372  
   373      // The following properties are node-fetch extensions
   374      follow: 20,         // maximum redirect count. 0 to not follow redirect
   375      timeout: 0,         // req/res timeout in ms, it resets on redirect. 0 to disable (OS limit applies). Signal is recommended instead.
   376      compress: true,     // support gzip/deflate content encoding. false to disable
   377      size: 0,            // maximum response body size in bytes. 0 to disable
   378      agent: null         // http(s).Agent instance or function that returns an instance (see below)
   379  }
   380  ```
   381  
   382  ##### Default Headers
   383  
   384  If no values are set, the following request headers will be sent automatically:
   385  
   386  Header              | Value
   387  ------------------- | --------------------------------------------------------
   388  `Accept-Encoding`   | `gzip,deflate` _(when `options.compress === true`)_
   389  `Accept`            | `*/*`
   390  `Connection`        | `close` _(when no `options.agent` is present)_
   391  `Content-Length`    | _(automatically calculated, if possible)_
   392  `Transfer-Encoding` | `chunked` _(when `req.body` is a stream)_
   393  `User-Agent`        | `node-fetch/1.0 (+https://github.com/bitinn/node-fetch)`
   394  
   395  Note: when `body` is a `Stream`, `Content-Length` is not set automatically.
   396  
   397  ##### Custom Agent
   398  
   399  The `agent` option allows you to specify networking related options which are out of the scope of Fetch, including and not limited to the following:
   400  
   401  - Support self-signed certificate
   402  - Use only IPv4 or IPv6
   403  - Custom DNS Lookup
   404  
   405  See [`http.Agent`](https://nodejs.org/api/http.html#http_new_agent_options) for more information.
   406  
   407  In addition, the `agent` option accepts a function that returns `http`(s)`.Agent` instance given current [URL](https://nodejs.org/api/url.html), this is useful during a redirection chain across HTTP and HTTPS protocol.
   408  
   409  ```js
   410  const httpAgent = new http.Agent({
   411      keepAlive: true
   412  });
   413  const httpsAgent = new https.Agent({
   414      keepAlive: true
   415  });
   416  
   417  const options = {
   418      agent: function (_parsedURL) {
   419          if (_parsedURL.protocol == 'http:') {
   420              return httpAgent;
   421          } else {
   422              return httpsAgent;
   423          }
   424      }
   425  }
   426  ```
   427  
   428  <a id="class-request"></a>
   429  ### Class: Request
   430  
   431  An HTTP(S) request containing information about URL, method, headers, and the body. This class implements the [Body](#iface-body) interface.
   432  
   433  Due to the nature of Node.js, the following properties are not implemented at this moment:
   434  
   435  - `type`
   436  - `destination`
   437  - `referrer`
   438  - `referrerPolicy`
   439  - `mode`
   440  - `credentials`
   441  - `cache`
   442  - `integrity`
   443  - `keepalive`
   444  
   445  The following node-fetch extension properties are provided:
   446  
   447  - `follow`
   448  - `compress`
   449  - `counter`
   450  - `agent`
   451  
   452  See [options](#fetch-options) for exact meaning of these extensions.
   453  
   454  #### new Request(input[, options])
   455  
   456  <small>*(spec-compliant)*</small>
   457  
   458  - `input` A string representing a URL, or another `Request` (which will be cloned)
   459  - `options` [Options][#fetch-options] for the HTTP(S) request
   460  
   461  Constructs a new `Request` object. The constructor is identical to that in the [browser](https://developer.mozilla.org/en-US/docs/Web/API/Request/Request).
   462  
   463  In most cases, directly `fetch(url, options)` is simpler than creating a `Request` object.
   464  
   465  <a id="class-response"></a>
   466  ### Class: Response
   467  
   468  An HTTP(S) response. This class implements the [Body](#iface-body) interface.
   469  
   470  The following properties are not implemented in node-fetch at this moment:
   471  
   472  - `Response.error()`
   473  - `Response.redirect()`
   474  - `type`
   475  - `trailer`
   476  
   477  #### new Response([body[, options]])
   478  
   479  <small>*(spec-compliant)*</small>
   480  
   481  - `body` A `String` or [`Readable` stream][node-readable]
   482  - `options` A [`ResponseInit`][response-init] options dictionary
   483  
   484  Constructs a new `Response` object. The constructor is identical to that in the [browser](https://developer.mozilla.org/en-US/docs/Web/API/Response/Response).
   485  
   486  Because Node.js does not implement service workers (for which this class was designed), one rarely has to construct a `Response` directly.
   487  
   488  #### response.ok
   489  
   490  <small>*(spec-compliant)*</small>
   491  
   492  Convenience property representing if the request ended normally. Will evaluate to true if the response status was greater than or equal to 200 but smaller than 300.
   493  
   494  #### response.redirected
   495  
   496  <small>*(spec-compliant)*</small>
   497  
   498  Convenience property representing if the request has been redirected at least once. Will evaluate to true if the internal redirect counter is greater than 0.
   499  
   500  <a id="class-headers"></a>
   501  ### Class: Headers
   502  
   503  This class allows manipulating and iterating over a set of HTTP headers. All methods specified in the [Fetch Standard][whatwg-fetch] are implemented.
   504  
   505  #### new Headers([init])
   506  
   507  <small>*(spec-compliant)*</small>
   508  
   509  - `init` Optional argument to pre-fill the `Headers` object
   510  
   511  Construct a new `Headers` object. `init` can be either `null`, a `Headers` object, an key-value map object or any iterable object.
   512  
   513  ```js
   514  // Example adapted from https://fetch.spec.whatwg.org/#example-headers-class
   515  
   516  const meta = {
   517    'Content-Type': 'text/xml',
   518    'Breaking-Bad': '<3'
   519  };
   520  const headers = new Headers(meta);
   521  
   522  // The above is equivalent to
   523  const meta = [
   524    [ 'Content-Type', 'text/xml' ],
   525    [ 'Breaking-Bad', '<3' ]
   526  ];
   527  const headers = new Headers(meta);
   528  
   529  // You can in fact use any iterable objects, like a Map or even another Headers
   530  const meta = new Map();
   531  meta.set('Content-Type', 'text/xml');
   532  meta.set('Breaking-Bad', '<3');
   533  const headers = new Headers(meta);
   534  const copyOfHeaders = new Headers(headers);
   535  ```
   536  
   537  <a id="iface-body"></a>
   538  ### Interface: Body
   539  
   540  `Body` is an abstract interface with methods that are applicable to both `Request` and `Response` classes.
   541  
   542  The following methods are not yet implemented in node-fetch at this moment:
   543  
   544  - `formData()`
   545  
   546  #### body.body
   547  
   548  <small>*(deviation from spec)*</small>
   549  
   550  * Node.js [`Readable` stream][node-readable]
   551  
   552  Data are encapsulated in the `Body` object. Note that while the [Fetch Standard][whatwg-fetch] requires the property to always be a WHATWG `ReadableStream`, in node-fetch it is a Node.js [`Readable` stream][node-readable].
   553  
   554  #### body.bodyUsed
   555  
   556  <small>*(spec-compliant)*</small>
   557  
   558  * `Boolean`
   559  
   560  A boolean property for if this body has been consumed. Per the specs, a consumed body cannot be used again.
   561  
   562  #### body.arrayBuffer()
   563  #### body.blob()
   564  #### body.json()
   565  #### body.text()
   566  
   567  <small>*(spec-compliant)*</small>
   568  
   569  * Returns: <code>Promise</code>
   570  
   571  Consume the body and return a promise that will resolve to one of these formats.
   572  
   573  #### body.buffer()
   574  
   575  <small>*(node-fetch extension)*</small>
   576  
   577  * Returns: <code>Promise&lt;Buffer&gt;</code>
   578  
   579  Consume the body and return a promise that will resolve to a Buffer.
   580  
   581  #### body.textConverted()
   582  
   583  <small>*(node-fetch extension)*</small>
   584  
   585  * Returns: <code>Promise&lt;String&gt;</code>
   586  
   587  Identical to `body.text()`, except instead of always converting to UTF-8, encoding sniffing will be performed and text converted to UTF-8 if possible.
   588  
   589  (This API requires an optional dependency of the npm package [encoding](https://www.npmjs.com/package/encoding), which you need to install manually. `webpack` users may see [a warning message](https://github.com/bitinn/node-fetch/issues/412#issuecomment-379007792) due to this optional dependency.)
   590  
   591  <a id="class-fetcherror"></a>
   592  ### Class: FetchError
   593  
   594  <small>*(node-fetch extension)*</small>
   595  
   596  An operational error in the fetching process. See [ERROR-HANDLING.md][] for more info.
   597  
   598  <a id="class-aborterror"></a>
   599  ### Class: AbortError
   600  
   601  <small>*(node-fetch extension)*</small>
   602  
   603  An Error thrown when the request is aborted in response to an `AbortSignal`'s `abort` event. It has a `name` property of `AbortError`. See [ERROR-HANDLING.MD][] for more info.
   604  
   605  ## Acknowledgement
   606  
   607  Thanks to [github/fetch](https://github.com/github/fetch) for providing a solid implementation reference.
   608  
   609  `node-fetch` v1 was maintained by [@bitinn](https://github.com/bitinn); v2 was maintained by [@TimothyGu](https://github.com/timothygu), [@bitinn](https://github.com/bitinn) and [@jimmywarting](https://github.com/jimmywarting); v2 readme is written by [@jkantr](https://github.com/jkantr).
   610  
   611  ## License
   612  
   613  MIT
   614  
   615  [npm-image]: https://flat.badgen.net/npm/v/node-fetch
   616  [npm-url]: https://www.npmjs.com/package/node-fetch
   617  [travis-image]: https://flat.badgen.net/travis/bitinn/node-fetch
   618  [travis-url]: https://travis-ci.org/bitinn/node-fetch
   619  [codecov-image]: https://flat.badgen.net/codecov/c/github/bitinn/node-fetch/master
   620  [codecov-url]: https://codecov.io/gh/bitinn/node-fetch
   621  [install-size-image]: https://flat.badgen.net/packagephobia/install/node-fetch
   622  [install-size-url]: https://packagephobia.now.sh/result?p=node-fetch
   623  [discord-image]: https://img.shields.io/discord/619915844268326952?color=%237289DA&label=Discord&style=flat-square
   624  [discord-url]: https://discord.gg/Zxbndcm
   625  [opencollective-image]: https://opencollective.com/node-fetch/backers.svg
   626  [opencollective-url]: https://opencollective.com/node-fetch
   627  [whatwg-fetch]: https://fetch.spec.whatwg.org/
   628  [response-init]: https://fetch.spec.whatwg.org/#responseinit
   629  [node-readable]: https://nodejs.org/api/stream.html#stream_readable_streams
   630  [mdn-headers]: https://developer.mozilla.org/en-US/docs/Web/API/Headers
   631  [LIMITS.md]: https://github.com/bitinn/node-fetch/blob/master/LIMITS.md
   632  [ERROR-HANDLING.md]: https://github.com/bitinn/node-fetch/blob/master/ERROR-HANDLING.md
   633  [UPGRADE-GUIDE.md]: https://github.com/bitinn/node-fetch/blob/master/UPGRADE-GUIDE.md