# Changelog
---------

### 5.2.0 (July 28, 2019)
- Added API example: Google Drive (thanks to @tanaydin)
- Added Google Sheets API example (thanks to @clarkngo)
- Added HERE Maps API example
- Added support for Intuit Quickbooks API
- Improved Lob.com API example
- Added support for email verification
- Added support for refreshing OAuth tokens
- Fixed bug when users attempt to login by email for accounts that are created with a sign in provider
- Fixed bug in the password reset
- Added CSRF check to the File Upload API example -- security improvement -- breaking change
- Added validation check to password reset token -- security improvement
- Fixed missing await in the Foursquare API example
- Fixed Google Oauth2 profile picture (thanks to @tanaydin)
- Removed deprecated Instagram API calls -- breaking change
- Upgrade to login by LinkedIn v2, remove LinkedIn API example -- breaking change
- Removed express-validator in favor of validator.js -- breaking change
- Removed Aviary API example since the service has been shutdown
- Added additional unit tests for the user model (thanks to @Tolsee)
- Updated Steam's logo
- Updated dependencies
- Updated documentation (thanks in part to @TheMissingNTLDR, @Coteh)

### 5.1.4 (May 14, 2019)
- Migrate from requestjs to axios (thanks to @FX-Wood)
- Enable page templates to add items to the HTML head element
- Fix bold font issue on macs (thanks to @neighlyd)
- Use BASE_URL for github
- Update min node engine to require Feb 2019 NodeJS security release
- Add Node.js 12 to the travis build
- Update dependencies
- Update documentation (thanks in part to @anubhavsrivastava, @Fullchee, @luckymurari)

### 5.1.3 (April 7, 2019)
- Update Steam API Integration
- Upgrade flatly theme files to 4.3.1
- Migrate from bcrypt-nodejs to bcrypt
- Use BASE_URL for twitter and facebook callbacks
- Add a ChartJS example in combination with Alpha Vantage API usage (thanks to @T-travis)
- Improve Github integration – use the user’s private email address if there is no public email listed (thanks to @danielhunt)
- Improve the error handling for the NYT API Example
- Add lodash 4.7
- Fixed gender radio buttons spacing
- Fixed alignment Issue for login / sign in buttons at certain screen widths. (thanks to @eric-sciberras)
- Remove Mozilla Persona information from README since it has been deprecated
- Remove utils
- Remove GSDK since it does not support Bootstrap 4(thanks to @laurenquinn5924)
- Adding additional tests to cover some of the API examples
- Add prod-checklist.md
- Update dependencies
- Update documentation (thanks in part to @GregBrimble)

### 5.1.2 (January 13, 2019)
- Added Login by Snapchat (thanks to @nicholasgonzalezsc)
- Migrate the Foursquare API example to use Axios calls instead of the npm library.
- Fixed minor visual issue in the web scraping example.
- Fixed issue with Popper.js integration (thanks to @binarymax and @Furchin)
- Fixed wrapping issues in the navbar and logo indentation (thanks to @estevanmaito)
- Fixed MongoDB deprecation warnings
- Add production error handler middleware that returns 500 to handle errors.  Also, handle server errors in the lastfm API example (thanks to @jagatfx)
- Added autocomplete properties to the views to address Chrome warnings (thanks to @peterblazejewicz)
- Fixed issues in the unit tests.
- Fixed issues in the modern theme variables and imports to be consistent (thanks to @monkeywithacupcake)
- Upgraded to Fontawesome to the latest version (thanks in part to @gesa)
- Upgraded eslint to v5.
- Updated dependencies
- Updated copyright year to include 2019
- Minor code formatting improvements
- Replaced mLab instructions with MongoDB Atlas instructions (thanks to @mgautam98)
- Fixed issues in the readme (thanks to @nero-adaware , @empurium, @aschwtzr)

### 5.1.1 (July 5, 2018)
- Upgraded FontAwesome to FontAwesome v5.1 - FontAwsome is now integrated using its npm package
- Fixed bug with JS libraries missing in Windows Dev envs
- Enabled autofocus in the Contact view when the user is logged in
- Fixed Home always being active (@dkimot)
- Modified Lob example to address recent API changes
- Updated Twilio API (@garretthogan)
- Fixed Twitter API (@garretthogan)
- Dependency updates

### 5.1.0 (May 9, 2018)
- Bootstrap 4.1 upgrade (breaking change)
- Addition of popper.js
- jQuery and Bootstrap will be pulled in the project using their npm packages
- Dockerfile will use development instead of production
- Security improvement by removing X-Powered-By header
- Express errorhandler will only be used in development to match its documentation
- Removed deprecated Instagram popular images API call from the Instagram example (@nacimgoura)
- Removed `mongoose global.Promise` as it is no longer needed (@nacimgoura)
- Refactoring of GitHub, last.fm api, twitter examples and code improvements to use ES6/ES7 features (@nacimgoura)
- Add NodeJS 10 in travis.yml (@nacimgoura)
- Improvements to the Steam API example (@nacimgoura)
- Readme and documentation improvements (thanks in part to @nacimgoura)
- Dependency updates

### 5.0.0 (April 1, 2018)
- NodeJS 8.0+ is now required
- Removed dependency on Bluebird in favor of native NodeJS promisify support
- Font awesome 5 Upgrade
- Fix console warning about Foursquare API version
- Added environment configs to eslint configs and cleaned up code (Thanks to @nacimgoura)
- Fixed eslint rules to better match the project
- Fixed Instagram API example view (@nacimgoura)
- Adding additional code editor related files to .gitignore (@nacimgoura)
- Upgraded syntax at various places to use ES6 syntax (Thanks to @nacimgoura)
- Re-added travis-ci.yml (Thanks to @nacimgoura)
- Fixed bug in Steam API when the user had no achievements (Thanks to @nacimgoura)
- Readme and documentation improvements
- Dependency updates

### 4.4.0 (March 23, 2018)
- Added Docker support (Thanks to @gregorysobotka, @praveenweb, @ryanhanwu).  The initial integration has also been upgraded to use NodeJS 8 and Mongo 3.6.
- Removed dependency on async in favor of using promises (@fmcarvalho). Note that the promise support will be upgraded in the upcoming releases to remove the use of Bluebird.
- The contact form will no longer ask for the user's name and email address if they have logged-in already
- Adding a confirmation prompt when a user asks for their account to be deleted
- Fixed Steam Oauth and API integration
- Fixed Last.fm API example (@JonLim)
- Fixed Google Map integration example (@whmsysu)
- Fixed Twitter API integration (@shahzeb1)
- Fixed Facebook integration/request scope (@RobTS)
- Removed MONGOLAB_URI env var, use MONGODB_URI instead
- Preserve the query parameters during authentication session returns (@shreedharshetty)
- normalizeEmail options key remove_dots changed to gmail_remove_dots (@amakhnev)
- Fixed Heroku re-deploy issue (@gballet)
- Migrated from Jade to Pug
- Migrated from GitHub npm package to @octokit/rest to address the related deprecation warning. See https://git.io/vNB11
- Dependency update and upgrades
- Updated left over port 3000 to the current default of port of 8080
- Removed bitgo.pug since bitgo has not been supported by hackathon-starter since v4.1.0
- Removed bitgo from api/index view (@JonLim)
- Fixed unsecure external content by switching them to https
- New address for the Live Demo site
- Code formatting, text prompt, and Readme improvements

### 4.3.0 (November 6, 2016)
- [Added new theme](http://demos.creative-tim.com/get-shit-done/index.html) by Creative Tim (Thanks @conacelelena)
- Added ESLint configuration to *package.json*
- Added *yarn.lock* (Thanks @niallobrien)
- Added **express-status-monitor** (to see it in action: `/status`)
- Added missing error handling checks (Thanks @dskrepps)
- Server address during the app startup is now clickable (⌘ + LMB) (Thanks @niallobrien)
- Fixed redirect issue in the account page (Thanks @YasharF)
- Fixed `Mongoose.promise` issue (Thanks @starcharles)
- Removed "My Friends" from Facebook API example due to Graph API changes
- Removed iOS7 theme
- `User` model unit tests improvements (Thanks @andela-rekemezie)
- Switched from **github-api** to the more popular **github** NPM module
- Updated Yarn and NPM dependencies

### 4.2.1 (September 6, 2016)
- User model minor code refactoring
- Fixed gravatar display issue on the profile page
- Pretty terminal logs for database connection and app server
- Added compiled *main.css* to *.gitignore*

### 4.2.0 (August 21, 2016)
- Converted templates from jade to pug (See [Rename from "Jade"](https://github.com/pugjs/pug#rename-from-jade))

### 4.1.1 (August 20, 2016)
- Updated dependencies

### 4.1.0 (July 23, 2016)
- Improved redirect logic after login [#435](https://github.com/sahat/hackathon-starter/pull/435)
- Removed Venmo API (see [Venmo Halts New Developer Access To Its API](https://techcrunch.com/2016/02/26/how-not-to-run-a-platform/))
- Removed BitGo API due to issues with `secp256k1` dependency on Windows

### 4.0.1 (May 17, 2016)
- Renamed `MONGODB` to `MONGODB_URI` environment variable
- Set engine `"node": "6.1.0"` in *package.json*

### 4.0.0 (May 13, 2016)
- **ECMAScript 2015 support!** (Make sure you are using Node.js 6.0+)
 - Thanks  @vanshady and @prashcr
- Added `<meta theme-color>` support for *Chrome for Android*
- Added Yahoo Finance API example
- Updated Aviary API example
- Flash an error message when updating email to that which is already taken
- Removing an email address during profile update is no longer possible
- PayPal API example now uses *return_url* and *cancel_url* from `.env`
- Added client-side `required=true` attributes to input fields
- Fixed broken `show()` function in the GitHub API example
- Fixed YQL query in the Yahoo Weather API example
- Fixed *Can't set headers after they are sent* error in Stripe API example
- Code refactoring and cleanup
- Updated Travis-CI Node.js version
- Updated NPM dependencies
- Removed Mandrill references

### 3.5.0 (March 4, 2016)
- Added file upload example
- Added Pinterest API example
- Added timestamp support to the User schema
- Fixed `next` parameter being *undefined* inside `getReset` handler
- Refactored querysting param usage in *api.js* controller
- Removed *setup.js* (generator) due to its limited functionality and a lack of updates

### 3.4.1 (February 6, 2016)
- Added "Obtaining Twilio API Keys" instructions.
- Updated Bootstrap v3.3.6.
- Updated jQuery v2.2.0.
- Updated Font Awesome v4.5.0.
- Removed `debug` and `outputStyle` from the Sass middleware options.
- Removed `connect-assets` (no longer used) from *package.json*`.
- Fixed Font Awesome icon syntax error in *profile.jade*.
- Fixed Cheerio broken link.

### 3.4.0 (January 5, 2016)
- Use `dontenv` package for managing API keys and secrets.
- Removed *secrets.js* (replaced by *.env.example*).
- Added .env to .gitignore.
- Fixed broken Aviary API image.

### 3.3.1 (December 25, 2015)
- Use `connect-mongo` ES5 fallback for backward-compatibility with Node.js version `< 4.0`.

### 3.3.0 (December 19, 2015)
- Steam authorization via OpenID.
- Code style update. (No longer use "one-liners" without braces)
- Updated LinkedIn scope from `r_fullprofile` to `r_basicprofile` due to API changes.
- Added LICENSE file.
- Removed [Bitcore](https://bitcore.io/) example due to installation issues on Windows 10.

### 3.2.0 (October 19, 2015)
- Added Google Analytics script.
- Split *api.js* `require` intro declaration and initialization for better performance. (See #247)
- Removed [ionicons](http://ionicons.com).
- Removed [connect-assets](https://github.com/adunkman/connect-assets). (Replaced by [node-sass-middleware](https://github.com/sass/node-sass-middleware))
- Fixed alignment styling on /login, /profile and /account
- Fixed Stripe API `POST` request.
- Converted LESS to Sass stylesheets.
- Set `node_js` version to "stable" in *.travis.yml*.
- Removed `mocha.opts` file, pass options directly to package.json
- README cleanup and fixes.
- Updated Font Awesome to 4.4.0

### 3.1.0 (August 25, 2015)
- Added Bitcore example.
- Added Bitgo example.
- Lots of README fixes.
- Fixed Google OAuth profile image url.
- Fixed a bug where `connect-assets` served all JS assets twice.
- Fixed missing `csrf` token in the Twilio API example form.
- Removed `multer` middleware.
- Removed Ordrx API. (Shutdown)

### 3.0.3 (May 14, 2015)
- Added favicon.
- Fixed an email issue with Google login.

### 3.0.2 (March 31, 2015)
- Renamed `navbar.jade` to `header.jade`.
- Fixed typos in README. Thanks @josephahn and @rstormsf.
- Fix radio button alignment on small screens in Profile page.
- Increased `bcrypt.genSalt()` from **5** to **10**.
- Updated package dependencies.
- Updated Font Awesome `4.3.0`.
- Updated Bootstrap `3.3.4`.
- Removed Ionicons.
- Removed unused `User` variable in *controllers/api.js*.
- Removed Nodejitsu instructions from README.

### 3.0.1 (February 23, 2015)
- Reverted Sass to LESS stylesheets. See #233.
- Convert email to lower case in Passport's LocalStrategy during login.
- New Lob API.
- Updated Font Awesome to 4.3.0
- Updated Bootstrap and Flatly theme to 3.3.2.

### 3.0.0 (January 11, 2015)
- New Ordr.in API example.
- Brought back PayPal API example.
- Added `xframe` and xssProtection` protection via **lusca** module.
- No more CSRF route whitelisting, either enable or dsiable it globally.
- Simplified "remember original destination" middleware.
 - Instead of excluding certain routes, you now have to "opt-in" for the routes you wish to remember for a redirect after successful authentication.
- Converted LESS to Sass.
- Updated Bootstrap to 3.3.1 and Font Awesome to 4.2.0.
- Updated jQuery to 2.1.3 and Bootstrap to 3.3.1 JS files.
- Updated Ionicons to 2.0.
- Faster travis-ci builds using `sudo: false`.
- Fixed YUI url on Yahoo API example.
- Fixed `mongo-connect` deprecation warning.
- Code cleanup throughout the project.
- Updated `secrets.js` notice.
- Simplified the generator (`setup.js`), no longer removes auth providers.
- Added `git remote rm origin` to Getting Started instructions in README.

### 2.4.0 (November 8, 2014)
- Bootstrap 3.3.0.
- Flatly 3.3.0 theme.
- User model cleanup.
- Removed `helperContext` from connect-assets middleware.

### 2.3.4 (October 27, 2014)
- Font Awesome 4.2.0 [01e7bd5c09926911ca856fe4990e6067d9148694](https://github.com/sahat/hackathon-starter/commit/01e7bd5c09926911ca856fe4990e6067d9148694)
- Code cleanup in `app.js` and `controllers/api.js`. [8ce48f767c0146062296685cc101acf3d5d224d9](https://github.com/sahat/hackathon-starter/commit/8ce48f767c0146062296685cc101acf3d5d224d9) [cdbb9d1888a96bbba92d4d14deec99a8acba2618](https://github.com/sahat/hackathon-starter/commit/cdbb9d1888a96bbba92d4d14deec99a8acba2618)
- Updated Stripe API example. [afef373cd57b6a44bf856eb093e8f2801fc2dbe2](https://github.com/sahat/hackathon-starter/commit/afef373cd57b6a44bf856eb093e8f2801fc2dbe2)
- Added 1-step deployment process with Heroku and mLab add-on. [c5def7b7b3b98462e9a2e7896dc11aaec1a48b3f](https://github.com/sahat/hackathon-starter/commit/c5def7b7b3b98462e9a2e7896dc11aaec1a48b3f)
- Updated Twitter apps dashboard url. [e378fbbc24e269de69494d326bc20fcb641c0697](https://github.com/sahat/hackathon-starter/commit/e378fbbc24e269de69494d326bc20fcb641c0697)
- Fixed dead links in the README. [78fac5489c596e8bcef0ab11a96e654335573bb4](https://github.com/sahat/hackathon-starter/commit/78fac5489c596e8bcef0ab11a96e654335573bb4)

### 2.3.3 (September 1, 2014)
- Use *https* (instead of http) profile image URL with Twitter authentication

### 2.3.2 (July 28, 2014)
- Fixed an issue with connect-assets when running `app.js` from an outside folder
- Temporarily disabled `setup.js` on Windows platform until [blessed](https://github.com/chjj/blessed) fixes its problems

### 2.3.1 (July 15, 2014)
- Migrated to Nodemailer 1.0

### 2.3 (July 2, 2014)
- Bootstrap 3.2
- New default theme
- Ionicons fonts
- Fixed bodyParser deprecation warning
- Minor visual updates
- CSS cleanup via RECESS
- Replaced `navbar-brand` image with a font icon

### 2.2.1 (June 17, 2014)
- Added IBM Codename: BlueMix deployment instructions

### 2.2 (June 6, 2014)
- Use Lodash instead of Underscore.js
- Replaced all occurrences of `_.findWhere` with `_.find`
- Added a flash message when user deletes an account
- Updated and clarified some comments
- Updated the Remove Auth message in `setup.js`
- Cleaned up `styles.less`
- Redesigned API Examples page
- Updated Last.fm API example
- Updated Steam API example
- Updated Instagram API example
- Updated Facebook API example
- Updated jQuery to 2.1.1
- Fixed a bug that didn't remove Instagram Auth properly
- Fixed Foursquare secret token

### 2.1.4 (June 5, 2014)
- Fixed a bug related to `returnTo` url (#155)

### 2.1.3 (June 3, 2014)
- Font Awesome 4.1
- Updated icons on some API examples
- Use LESS files for *bootstrap-social* and *font-awesome*

### 2.1.2 (June 2, 2014)
- Improved Twilio API example
- Updated dependencies

### 2.1.1 (May 29, 2014)
- Added **Compose new Tweet** to Twitter API example
- Fixed email service indentation
- Fixed Mailgun and Mandrill secret.js properties
- Renamed `navigation.jade` to `navbar.jade`

### 2.1 (May 13, 2014)
- New and improved generator - **setup.js**
- Added Yahoo API
- CSS and templates cleanup
- Minor improvement to the default theme
- `cluster_app.js` has been moved into **setup.js**

### 2.0.4 (April 26, 2014)
- Added Mandrill e-mail service (via generator)

### 2.0.3 (April 25, 2014)
- LinkedIn API: Fixed an error if a user did not specify education on LinkedIn
- Removed email constraint when linking OAuth accounts in order to be able to merge accounts that use the same email address
- Check if email address is already taken when creating a new local account
 - Previously relied on Validation Error 11000, which doesn't always work
- When creating a local account, checks if e-mail address is already taken
- Flash notifications can now be dismissed by clicking on �?

### 2.0.2 (April 22, 2014)
- Added Instagram Authentication
- Added Instagram API example
- Updated Instagram Strategy to use a "fake" email address similar to Twitter Startegy

### 2.0.1 (April 18, 2014)
- Conditional CSRF support using [lusca](https://github.com/krakenjs/lusca)
- Fixed EOL problem in `generator.js` for Windows users
- Fixed outdated csrf token string on profile.jade
- Code cleanup

### 2.0.0 (April 15, 2014)
There are have been over **500+** commits since the initial announcement in
January 2014 and over a **120** issues and pull requests from **28** contributors.

- Documentation grew **8x** in size since the announcement on Hacker News
- Upgraded to Express 4.0
- Generator for adding/removing authentication providers
- New Instagram authentication that can be added via generator
- Forgot password and password reset for Local authentication
- Added LinkedIn authentication and API example
- Added Stripe API example
- Added Venmo API example
- Added Clockwork SMS example
- Nicer Facebook API example
- Pre-populated secrets.js with API keys (not linked to my personal accounts)
- Grid layout with company logos on API Examples page
- Added tests (Mocha, Chai, Supertest)
- Gravatar pictures in Navbar and Profile page
- Tracks last visited URL before signing in to redirect back to original destination
- CSRF protection
- Gzip compression and static assets caching
- Client-side JavaScript is automatically minified+concatenated in production
- Navbar, flash messages, footer refactored into partial templates
- Support for Node.js clusters
- Support for Mailgun email service
- Support for environment variables in secrets.js
- Switched from less-middleware to connect-assets
- Bug fixes related to multi-authentication login and account linking
- Other small fixes and changes that are too many to list


| Will be mailed using: #{uspsLetter.mail_type}

keep keep keep keep keep replace keep keep keep keep keep

/* eslint-env jquery, browser */
$(document).ready(() => {

  // Place JavaScript code here...

});


keep keep keep replace keep keep keep keep keep

extends ../layout

block content
  .pb-2.mt-2.mb-4.border-bottom
    h2
      i.far.fa-envelope.fa-sm
      | Lob API
  .btn-group.d-flex(role='group')
    a.btn.btn-primary(href='https://lob.com/docs')
      i.far.fa-check-square.fa-sm
      | API Documentation
    a.btn.btn-primary(href='https://github.com/lob/lob-node', target='_blank').w-100
      i.fas.fa-code.fa-sm
      | Lob Node Docs
    a.btn.btn-primary(href='https://dashboard.lob.com/register', target='_blank').w-100
      i.fas.fa-cog.fa-sm
      | Create API Account
  .pb-2.mt-2.mb-4.border-bottom
  h3 Details of zip code: #{zipDetails.zip_code}
  br
  p Note that Lob.com's test API key does not perform any verification, automatic correction, or standardization for addresses. The responses from their test API will always be the response for
    a(href='https://lob.com/docs#us-verification-test-environment', target='_blank')
      |  PO BOX 720114, San Francisco, CA 94172-0114
  br
  p ID: #{zipDetails.id}
  p Zip Code Type: #{zipDetails.zip_code_type}
  table.table.table-striped.table-bordered
    thead
      tr
        th City
        th State
        th County
        th County Fips
        th Preferred
    tbody
      for cities in zipDetails.cities
        tr
          td= cities.city
          td= cities.state
          td= cities.county
          td= cities.county_fips
          td= cities.preferred
  .pb-2.mt-2.mb-4.border-bottom
  h3 Fistclass mail
  br
  | Letter ID: #{uspsLetter.id}
  br
  | Will be mailed using: #{uspsLetter.mail_type}
  br
  | With expected delivery date of: #{uspsLetter.expected_delivery_date}
  #pdfviewer(style='display:none')
    object(width='600', height='850', type='application/pdf', data=uspsLetter.url)

  //Lob's back end has a few second delay from when they generate the letter to when it is availalble
  //Without this delay some of the PDF fetches result in 404s
  script(type='text/javascript').
    window.onload = function ()
    {
    setTimeout(function(){
    $("#pdfviewer").show();
    }, 3000);
    };


The MIT License (MIT)

Copyright (c) 2014-2019 Sahat Yalkabov

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.


extends ../layout

block content
  .pb-2.mt-2.mb-4.border-bottom
    h2
      i.fab.fa-tumblr-square.fa-sm
      | Tumblr API

  .btn-group.d-flex(role='group')
    a.btn.btn-primary(href='http://www.tumblr.com/docs/en/api/v2#overview', target='_blank').w-100
      i.far.fa-check-square.fa-sm
      | Overview
    a.btn.btn-primary(href='https://api.tumblr.com/console', target='_blank').w-100
      i.fas.fa-laptop.fa-sm
      | API Console
    a.btn.btn-primary(href='http://www.tumblr.com/docs/en/api/v2#blog_methods', target='_blank').w-100
      i.fas.fa-code-branch.fa-sm
      | API Endpoints

  h3.text-primary #{blog.name}'s blog
  .btn.btn-raised
    i.far.fa-file-alt.fa-sm
    | #{blog.posts} posts
  h4 Latest Photo Post
  for photo in photoset
    img.item(src=photo.original_size.url)


// Update parent component's _dom and c.base pointers

// Note: parent component's _lastDomChild may become out of date, but that is okay,

// because _lastDomChild isn't used to restart diffs. It is only important while a

// diff for that component is happening and the diff will reset it to the correct value

&& vnode._parent._children.indexOf(vnode) == 0

keep keep keep replace replace replace replace keep keep keep

import { diff, unmount, applyRef } from './index';
import { coerceToVNode } from '../create-element';
import { EMPTY_OBJ, EMPTY_ARR } from '../constants';
import { removeNode } from '../util';

/**
 * Diff the children of a virtual node
 * @param {import('../internal').PreactElement} parentDom The DOM element whose
 * children are being diffed
 * @param {import('../internal').VNode} newParentVNode The new virtual
 * node whose children should be diff'ed against oldParentVNode
 * @param {import('../internal').VNode} oldParentVNode The old virtual
 * node whose children should be diff'ed against newParentVNode
 * @param {object} context The current context object
 * @param {boolean} isSvg Whether or not this DOM node is an SVG node
 * @param {Array<import('../internal').PreactElement>} excessDomChildren
 * @param {Array<import('../internal').Component>} mounts The list of components
 * which have mounted
 * @param {Node | Text} oldDom The current attached DOM
 * element any new dom elements should be placed around. Likely `null` on first
 * render (except when hydrating). Can be a sibling DOM element when diffing
 * Fragments that have siblings. In most cases, it starts out as `oldChildren[0]._dom`.
 */
export function diffChildren(parentDom, newParentVNode, oldParentVNode, context, isSvg, excessDomChildren, mounts, oldDom) {
	let childVNode, i, j, oldVNode, newDom, sibDom, firstChildDom, refs;

	let newChildren = newParentVNode._children || toChildArray(newParentVNode.props.children, newParentVNode._children=[], coerceToVNode, true);
	// This is a compression of oldParentVNode!=null && oldParentVNode != EMPTY_OBJ && oldParentVNode._children || EMPTY_ARR
	// as EMPTY_OBJ._children should be `undefined`.
	let oldChildren = (oldParentVNode && oldParentVNode._children) || EMPTY_ARR;

	let oldChildrenLength = oldChildren.length;

	// Only in very specific places should this logic be invoked (top level `render` and `diffElementNodes`).
	// I'm using `EMPTY_OBJ` to signal when `diffChildren` is invoked in these situations. I can't use `null`
	// for this purpose, because `null` is a valid value for `oldDom` which can mean to skip to this logic
	// (e.g. if mounting a new tree in which the old DOM should be ignored (usually for Fragments).
	if (oldDom == EMPTY_OBJ) {
		oldDom = null;
		if (excessDomChildren!=null) {
			for (i = 0; !oldDom && i < excessDomChildren.length; i++) {
				oldDom = excessDomChildren[i];
			}
		}
		else {
			for (i = 0; !oldDom && i < oldChildrenLength; i++) {
				oldDom = oldChildren[i] && oldChildren[i]._dom;
			}
		}
	}

	for (i=0; i<newChildren.length; i++) {
		childVNode = newChildren[i] = coerceToVNode(newChildren[i]);

		if (childVNode!=null) {
			childVNode._parent = newParentVNode;
			childVNode._depth = newParentVNode._depth + 1;

			// Check if we find a corresponding element in oldChildren.
			// If found, delete the array item by setting to `undefined`.
			// We use `undefined`, as `null` is reserved for empty placeholders
			// (holes).
			oldVNode = oldChildren[i];

			if (oldVNode===null || (oldVNode && childVNode.key == oldVNode.key && childVNode.type === oldVNode.type)) {
				oldChildren[i] = undefined;
			}
			else {
				// Either oldVNode === undefined or oldChildrenLength > 0,
				// so after this loop oldVNode == null or oldVNode is a valid value.
				for (j=0; j<oldChildrenLength; j++) {
					oldVNode = oldChildren[j];
					// If childVNode is unkeyed, we only match similarly unkeyed nodes, otherwise we match by key.
					// We always match by type (in either case).
					if (oldVNode && childVNode.key == oldVNode.key && childVNode.type === oldVNode.type) {
						oldChildren[j] = undefined;
						break;
					}
					oldVNode = null;
				}
			}

			oldVNode = oldVNode || EMPTY_OBJ;

			// Morph the old element into the new one, but don't append it to the dom yet
			newDom = diff(parentDom, childVNode, oldVNode, context, isSvg, excessDomChildren, mounts, null, oldDom);

			if ((j = childVNode.ref) && oldVNode.ref != j) {
				(refs || (refs=[])).push(j, childVNode._component || newDom);
			}

			// Only proceed if the vnode has not been unmounted by `diff()` above.
			if (newDom!=null) {
				if (firstChildDom == null) {
					firstChildDom = newDom;
				}

				if (childVNode._lastDomChild != null) {
					// Only Fragments or components that return Fragment like VNodes will
					// have a non-null _lastDomChild. Continue the diff from the end of
					// this Fragment's DOM tree.
					newDom = childVNode._lastDomChild;
				}
				else if (excessDomChildren==oldVNode || newDom!=oldDom || newDom.parentNode==null) {
					// NOTE: excessDomChildren==oldVNode above:
					// This is a compression of excessDomChildren==null && oldVNode==null!
					// The values only have the same type when `null`.

					outer: if (oldDom==null || oldDom.parentNode!==parentDom) {
						parentDom.appendChild(newDom);
					}
					else {
						// `j<oldChildrenLength; j+=2` is an alternative to `j++<oldChildrenLength/2`
						for (sibDom=oldDom, j=0; (sibDom=sibDom.nextSibling) && j<oldChildrenLength; j+=2) {
							if (sibDom==newDom) {
								break outer;
							}
						}
						parentDom.insertBefore(newDom, oldDom);
					}
				}

				oldDom = newDom.nextSibling;

				if (typeof newParentVNode.type == 'function') {
					// At this point, if childVNode._lastDomChild existed, then
					// newDom = childVNode._lastDomChild per line 101
					newParentVNode._lastDomChild = newDom;
				}
			}
		}
	}

	newParentVNode._dom = firstChildDom;

	// Remove children that are not part of any vnode.
	if (excessDomChildren!=null && typeof newParentVNode.type !== 'function') for (i=excessDomChildren.length; i--; ) if (excessDomChildren[i]!=null) removeNode(excessDomChildren[i]);

	// Remove remaining oldChildren if there are any.
	for (i=oldChildrenLength; i--; ) if (oldChildren[i]!=null) unmount(oldChildren[i], newParentVNode);

	// Set refs only after unmount
	if (refs) {
		for (i = 0; i < refs.length; i++) {
			applyRef(refs[i], refs[++i], newParentVNode);
		}
	}
}

/**
 * Flatten a virtual nodes children to a single dimensional array
 * @param {import('../index').ComponentChildren} children The unflattened
 * children of a virtual node
 * @param {Array<import('../internal').VNode | null>} [flattened] An flat array of children to modify
 * @param {typeof import('../create-element').coerceToVNode} [map] Function that
 * will be applied on each child if the `vnode` is not `null`
 * @param {boolean} [keepHoles] wether to coerce `undefined` to `null` or not.
 * This is needed for Components without children like `<Foo />`.
 */
export function toChildArray(children, flattened, map, keepHoles) {
	if (flattened == null) flattened = [];
	if (children==null || typeof children === 'boolean') {
		if (keepHoles) flattened.push(null);
	}
	else if (Array.isArray(children)) {
		for (let i=0; i < children.length; i++) {
			toChildArray(children[i], flattened, map, keepHoles);
		}
	}
	else {
		flattened.push(map ? map(children) : children);
	}

	return flattened;
}


import { setupRerender } from 'preact/test-utils';
import { setupScratch, teardown } from '../../../test/_util/helpers';
import { Component, render, createElement as h, memo } from '../../src';

/** @jsx h */

describe('memo()', () => {
	let scratch, rerender;

	beforeEach(() => {
		scratch = setupScratch();
		rerender = setupRerender();
	});

	afterEach(() => {
		teardown(scratch);
	});

	it('should work with function components', () => {
		let spy = sinon.spy();

		function Foo() {
			spy();
			return
Hello World
;
		}

		let Memoized = memo(Foo);

		let update;
		class App extends Component {
			constructor() {
				super();
				update = () => this.setState({});
			}
			render() {
				return <Memoized />;
			}
		}
		render(<App />, scratch);

		expect(spy).to.be.calledOnce;

		update();
		rerender();

		expect(spy).to.be.calledOnce;
	});

	it('should support custom comparer functions', () => {
		function Foo() {
			return
Hello World
;
		}

		let spy = sinon.spy(() => true);
		let Memoized = memo(Foo, spy);

		let update;
		class App extends Component {
			constructor() {
				super();
				update = () => this.setState({});
			}
			render() {
				return <Memoized />;
			}
		}
		render(<App />, scratch);

		update();
		rerender();

		expect(spy).to.be.calledOnce;
		expect(spy).to.be.calledWith({}, {});
	});

	it('should rerender when custom comparer returns false', () => {
		const spy = sinon.spy();
		function Foo() {
			spy();
			return
Hello World
;
		}

		const App = memo(Foo, () => false);
		render(<App />, scratch);
		expect(spy).to.be.calledOnce;

		render(<App foo="bar" />, scratch);
		expect(spy).to.be.calledTwice;
	});

	it('should pass props and nextProps to comparer fn', () => {
		const spy = sinon.spy(() => false);
		function Foo() {
			return
foo
;
		}

		const props = { foo: true };
		const nextProps = { foo: false };
		const App = memo(Foo, spy);
		render(h(App, props), scratch);
		render(h(App, nextProps), scratch);

		expect(spy).to.be.calledWith(props, nextProps);
	});

	it('should nest without errors', () => {
		const Foo = () =>
foo
;
		const App = memo(memo(Foo));

		// eslint-disable-next-line prefer-arrow-callback
		expect(function() {
			render(<App />, scratch);
		}).to.not.throw();
	});
});


/* eslint no-console:0 */

// parent node types that we don't want to remove pointless initializations from (because it breaks hoisting)
const BLOCKED = ['ForStatement', 'WhileStatement']; // 'IfStatement', 'SwitchStatement'

/** Removes var initialization to `void 0`, which Babel adds for TDZ strictness. */
export default (file, api) => {
	let { jscodeshift } = api,
		found = 0;

	let code = jscodeshift(file.source)
		.find(jscodeshift.VariableDeclaration)
		.forEach(handleDeclaration);

	function handleDeclaration(decl) {
		let p = decl,
			remove = true;

		while ((p = p.parentPath)) {
			if (~BLOCKED.indexOf(p.value.type)) {
				remove = false;
				break;
			}
		}

		decl.value.declarations.filter(isPointless).forEach( node => {
			if (remove===false) {
				console.log(`> Skipping removal of undefined init for "${node.id.name}": within ${p.value.type}`);
			}
			else {
				removeNodeInitialization(node);
			}
		});
	}

	function removeNodeInitialization(node) {
		node.init = null;
		found++;
	}

	function isPointless(node) {
		let { init } = node;
		if (init) {
			if (init.type==='UnaryExpression' && init.operator==='void' && init.argument.value==0) {
				return true;
			}
			if (init.type==='Identifier' && init.name==='undefined') {
				return true;
			}
		}
		return false;
	}

	return found ? code.toSource({ quote: 'single' }) : null;
};


import { createElement as h, render } from 'preact';
import { setupScratch, teardown } from '../../../test/_util/helpers';
import { useMemo } from '../../src';

/** @jsx h */

describe('useMemo', () => {

	/** @type {HTMLDivElement} */
	let scratch;

	beforeEach(() => {
		scratch = setupScratch();
	});

	afterEach(() => {
		teardown(scratch);
	});

	it('only recomputes the result when inputs change', () => {
		let memoFunction = sinon.spy((a, b) => a + b);
		const results = [];

		function Comp({ a, b }) {
			const result = useMemo(() => memoFunction(a, b), [a, b]);
			results.push(result);
			return null;
		}

		render(<Comp a={1} b={1} />, scratch);
		render(<Comp a={1} b={1} />, scratch);

		expect(results).to.deep.equal([2, 2]);
		expect(memoFunction).to.have.been.calledOnce;

		render(<Comp a={1} b={2} />, scratch);
		render(<Comp a={1} b={2} />, scratch);

		expect(results).to.deep.equal([2, 2, 3, 3]);
		expect(memoFunction).to.have.been.calledTwice;
	});

});


// Only Fragments or components that return Fragment like VNodes will

// have a non-null _lastDomChild. Continue the diff from the end of

else if (excessDomChildren==oldVNode || newDom!=oldDom || newDom.parentNode==null) {

// NOTE: excessDomChildren==oldVNode above:

// Eagerly cleanup _lastDomChild. We don't need to persist the value because

// it is only used by `diffChildren` to determine where to resume the diff after

import { EMPTY_OBJ, EMPTY_ARR } from '../constants';
import { Component, enqueueRender } from '../component';
import { coerceToVNode, Fragment } from '../create-element';
import { diffChildren, toChildArray } from './children';
import { diffProps } from './props';
import { assign, removeNode } from '../util';
import options from '../options';

/**
 * Diff two virtual nodes and apply proper changes to the DOM
 * @param {import('../internal').PreactElement} parentDom The parent of the DOM element
 * @param {import('../internal').VNode | null} newVNode The new virtual node
 * @param {import('../internal').VNode | null} oldVNode The old virtual node
 * @param {object} context The current context object
 * @param {boolean} isSvg Whether or not this element is an SVG node
 * @param {Array<import('../internal').PreactElement>} excessDomChildren
 * @param {Array<import('../internal').Component>} mounts A list of newly
 * mounted components
 * @param {Element | Text} oldDom The current attached DOM
 * element any new dom elements should be placed around. Likely `null` on first
 * render (except when hydrating). Can be a sibling DOM element when diffing
 * Fragments that have siblings. In most cases, it starts out as `oldChildren[0]._dom`.
 */
export function diff(parentDom, newVNode, oldVNode, context, isSvg, excessDomChildren, mounts, force, oldDom) {
	let c, tmp, isNew, oldProps, oldState, snapshot,
		newType = newVNode.type, clearProcessingException;

	// When passing through createElement it assigns the object
	// constructor as undefined. This to prevent JSON-injection.
	if (newVNode.constructor !== undefined) return null;

	if (tmp = options._diff) tmp(newVNode);

	try {
		outer: if (typeof newType==='function') {

			// Necessary for createContext api. Setting this property will pass
			// the context value as `this.context` just for this component.
			tmp = newType.contextType;
			let provider = tmp && context[tmp._id];
			let cctx = tmp ? (provider ? provider.props.value : tmp._defaultValue) : context;

			// Get component and set it to `c`
			if (oldVNode._component) {
				c = newVNode._component = oldVNode._component;
				clearProcessingException = c._processingException = c._pendingError;
			}
			else {
				// Instantiate the new component
				if (newType.prototype && newType.prototype.render) {
					newVNode._component = c = new newType(newVNode.props, cctx); // eslint-disable-line new-cap
				}
				else {
					newVNode._component = c = new Component(newVNode.props, cctx);
					c.constructor = newType;
					c.render = doRender;
				}
				if (provider) provider.sub(c);

				c.props = newVNode.props;
				if (!c.state) c.state = {};
				c.context = cctx;
				c._context = context;
				isNew = c._dirty = true;
				c._renderCallbacks = [];
			}

			// Invoke getDerivedStateFromProps
			if (c._nextState==null) {
				c._nextState = c.state;
			}
			if (newType.getDerivedStateFromProps!=null) {
				assign(c._nextState==c.state ? (c._nextState = assign({}, c._nextState)) : c._nextState, newType.getDerivedStateFromProps(newVNode.props, c._nextState));
			}

			// Invoke pre-render lifecycle methods
			if (isNew) {
				if (newType.getDerivedStateFromProps==null && c.componentWillMount!=null) c.componentWillMount();
				if (c.componentDidMount!=null) mounts.push(c);
			}
			else {
				if (newType.getDerivedStateFromProps==null && force==null && c.componentWillReceiveProps!=null) {
					c.componentWillReceiveProps(newVNode.props, cctx);
				}

				if (!force && c.shouldComponentUpdate!=null && c.shouldComponentUpdate(newVNode.props, c._nextState, cctx)===false) {
					c.props = newVNode.props;
					c.state = c._nextState;
					c._dirty = false;
					c._vnode = newVNode;
					newVNode._dom = oldVNode._dom;
					newVNode._lastDomChild = oldVNode._lastDomChild;
					newVNode._children = oldVNode._children;
					break outer;
				}

				if (c.componentWillUpdate!=null) {
					c.componentWillUpdate(newVNode.props, c._nextState, cctx);
				}
			}

			oldProps = c.props;
			oldState = c.state;

			c.context = cctx;
			c.props = newVNode.props;
			c.state = c._nextState;

			if (tmp = options._render) tmp(newVNode);

			c._dirty = false;

			try {
				tmp = c.render(c.props, c.state, c.context);
				let isTopLevelFragment = tmp != null && tmp.type == Fragment && tmp.key == null;
				toChildArray(isTopLevelFragment ? tmp.props.children : tmp, newVNode._children=[], coerceToVNode, true);
			}
			catch (e) {
				if ((tmp = options._catchRender) && tmp(e, newVNode)) return;
				throw e;
			}

			if (c.getChildContext!=null) {
				context = assign(assign({}, context), c.getChildContext());
			}

			if (!isNew && c.getSnapshotBeforeUpdate!=null) {
				snapshot = c.getSnapshotBeforeUpdate(oldProps, oldState);
			}

			diffChildren(parentDom, newVNode, oldVNode, context, isSvg, excessDomChildren, mounts, oldDom);

			// Only change the fields on the component once they represent the new state of the DOM
			c.base = newVNode._dom;
			c._vnode = newVNode;
			c._parentDom = parentDom;

			while (tmp=c._renderCallbacks.pop()) tmp.call(c);

			// Don't call componentDidUpdate on mount or when we bailed out via
			// `shouldComponentUpdate`
			if (!isNew && oldProps!=null && c.componentDidUpdate!=null) {
				c.componentDidUpdate(oldProps, oldState, snapshot);
			}
		}
		else {
			newVNode._dom = diffElementNodes(oldVNode._dom, newVNode, oldVNode, context, isSvg, excessDomChildren, mounts);
		}

		if (clearProcessingException) {
			c._pendingError = c._processingException = null;
		}

		if (tmp = options.diffed) tmp(newVNode);
	}
	catch (e) {
		catchErrorInComponent(e, newVNode._parent);
	}

	return newVNode._dom;
}

export function commitRoot(mounts, root) {
	let c;
	while ((c = mounts.pop())) {
		try {
			c.componentDidMount();
		}
		catch (e) {
			catchErrorInComponent(e, c._vnode._parent);
		}
	}

	if (options._commit) options._commit(root);
}

/**
 * Diff two virtual nodes representing DOM element
 * @param {import('../internal').PreactElement} dom The DOM element representing
 * the virtual nodes being diffed
 * @param {import('../internal').VNode} newVNode The new virtual node
 * @param {import('../internal').VNode} oldVNode The old virtual node
 * @param {object} context The current context object
 * @param {boolean} isSvg Whether or not this DOM node is an SVG node
 * @param {*} excessDomChildren
 * @param {Array<import('../internal').Component>} mounts An array of newly
 * mounted components
 * @returns {import('../internal').PreactElement}
 */
function diffElementNodes(dom, newVNode, oldVNode, context, isSvg, excessDomChildren, mounts) {
	let i;
	let oldProps = oldVNode.props;
	let newProps = newVNode.props;

	// Tracks entering and exiting SVG namespace when descending through the tree.
	isSvg = newVNode.type==='svg' || isSvg;

	if (dom==null && excessDomChildren!=null) {
		for (i=0; i<excessDomChildren.length; i++) {
			const child = excessDomChildren[i];
			if (child!=null && (newVNode.type===null ? child.nodeType===3 : child.localName===newVNode.type)) {
				dom = child;
				excessDomChildren[i] = null;
				break;
			}
		}
	}

	if (dom==null) {
		if (newVNode.type===null) {
			return document.createTextNode(newProps);
		}
		dom = isSvg ? document.createElementNS('http://www.w3.org/2000/svg', newVNode.type) : document.createElement(newVNode.type);
		// we created a new parent, so none of the previously attached children can be reused:
		excessDomChildren = null;
	}

	if (newVNode.type===null) {
		if (oldProps !== newProps) {
			dom.data = newProps;
		}
	}
	else {
		if (excessDomChildren!=null && dom.childNodes!=null) {
			excessDomChildren = EMPTY_ARR.slice.call(dom.childNodes);
		}
		if (newVNode!==oldVNode) {
			let oldProps = oldVNode.props || EMPTY_OBJ;
			let newProps = newVNode.props;

			let oldHtml = oldProps.dangerouslySetInnerHTML;
			let newHtml = newProps.dangerouslySetInnerHTML;
			if ((newHtml || oldHtml) && excessDomChildren==null) {
				// Avoid re-applying the same '__html' if it did not changed between re-render
				if (!newHtml || !oldHtml || newHtml.__html!=oldHtml.__html) {
					dom.innerHTML = newHtml && newHtml.__html || '';
				}
			}
			if (newProps.multiple) {
				dom.multiple = newProps.multiple;
			}

			diffChildren(dom, newVNode, oldVNode, context, newVNode.type==='foreignObject' ? false : isSvg, excessDomChildren, mounts, EMPTY_OBJ);
			diffProps(dom, newProps, oldProps, isSvg);
		}
	}

	return dom;
}

/**
 * Invoke or update a ref, depending on whether it is a function or object ref.
 * @param {object|function} ref
 * @param {any} value
 * @param {import('../internal').VNode} parentVNode
 */
export function applyRef(ref, value, parentVNode) {
	try {
		if (typeof ref=='function') ref(value);
		else ref.current = value;
	}
	catch (e) {
		catchErrorInComponent(e, parentVNode);
	}
}

/**
 * Unmount a virtual node from the tree and apply DOM changes
 * @param {import('../internal').VNode} vnode The virtual node to unmount
 * @param {import('../internal').VNode} parentVNode The parent of the VNode that
 * initiated the unmount
 * @param {boolean} [skipRemove] Flag that indicates that a parent node of the
 * current element is already detached from the DOM.
 */
export function unmount(vnode, parentVNode, skipRemove) {
	let r;
	if (options.unmount) options.unmount(vnode);

	if (r = vnode.ref) {
		applyRef(r, null, parentVNode);
	}

	let dom;
	if (!skipRemove && typeof vnode.type !== 'function') {
		skipRemove = (dom = vnode._dom)!=null;
	}

	vnode._dom = vnode._lastDomChild = null;

	if ((r = vnode._component)!=null) {
		if (r.componentWillUnmount) {
			try {
				r.componentWillUnmount();
			}
			catch (e) {
				catchErrorInComponent(e, parentVNode);
			}
		}

		r.base = r._parentDom = null;
	}

	if (r = vnode._children) {
		for (let i = 0; i < r.length; i++) {
			if (r[i]) unmount(r[i], parentVNode, skipRemove);
		}
	}

	if (dom!=null) removeNode(dom);
}

/** The `.render()` method for a PFC backing instance. */
function doRender(props, state, context) {
	return this.constructor(props, context);
}

/**
 * Find the closest error boundary to a thrown error and call it
 * @param {object} error The thrown value
 * @param {import('../internal').VNode} vnode The first ancestor
 * VNode check for error boundary behaviors
 */
function catchErrorInComponent(error, vnode) {
	if (options._catchError) { options._catchError(error, vnode); }

	/** @type {import('../internal').Component} */
	let component;

	for (; vnode; vnode = vnode._parent) {
		if ((component = vnode._component) && !component._processingException) {
			try {
				if (component.constructor && component.constructor.getDerivedStateFromError!=null) {
					component.setState(component.constructor.getDerivedStateFromError(error));
				}
				else if (component.componentDidCatch!=null) {
					component.componentDidCatch(error);
				}
				else {
					continue;
				}
				return enqueueRender(component._pendingError = component);
			}
			catch (e) {
				error = e;
			}
		}
	}

	throw error;
}


export const EMPTY_OBJ = {};
export const EMPTY_ARR = [];
export const IS_NON_DIMENSIONAL = /acit|ex(?:s|g|n|p|$)|rph|grid|ows|mnc|ntw|ine[ch]|zoo|^ord|^--/i;


import htm from 'htm';
import { h } from 'preact';
import { useState, useCallback } from 'preact/hooks';

const html = htm.bind(h);

// configuration used to show behavior vs. workaround
let childFirst = true;
const Config = () => html`<label>
	<input type="checkbox"
    checked=${childFirst}
    onchange=${evt => {
		childFirst = evt.target.checked;
	}}
  />
		Set child state before parent state.
	</label>`;

const Child = ({ items,setItems }) => {
	let [pendingId, setPendingId] = useState(null);
	if (!pendingId) {
		setPendingId(pendingId = Math.random().toFixed(20).slice(2));
	}

	const onInput = useCallback(evt => {
		let val = evt.target.value,
			_items = [...items, { _id: pendingId, val }];
		if (childFirst) {
			setPendingId(null);
			setItems(_items);
		}
		else {
			setItems(_items);
			setPendingId(null);
		}
	}, [childFirst, setPendingId, setItems, items, pendingId]);

	return html`<div class="item-editor">
    ${items.map((item,idx) => html`<input key=${item._id}
      value=${item.val}
      oninput=${evt => {
		let val = evt.target.value,
			_items = [...items];
		_items.splice(idx, 1, { ...item,val });
		setItems(_items);
	}}
    />`)}

    <input key=${pendingId}
      placeholder="type to add an item"
      oninput=${onInput}
    />
  </div>`;
};

const Parent = () => {
	let [items, setItems] = useState([]);
	return html`<div><${Config} /><${Child} items=${items} setItems=${setItems} /></div>`;
};

export default Parent;


# People demo page

This section of our demo was originally made by [phaux](https://github.com/phaux) in the [web-app-boilerplate](https://github.com/phaux/web-app-boilerplate) repo. It has been slightly modified from it's original to better work inside of our demo app


if (typeof newParentVNode.type == 'function') {

// At this point, if childVNode._lastDomChild existed, then

keep keep keep keep keep replace keep keep keep

// newDom = childVNode._lastDomChild per line 101. Else it is

// the same as childVNode._dom, meaning this component returned


import { Ref } from '../..';
import {
  Component as PreactComponent,
  VNode as PreactVNode,
  FunctionalComponent as PreactFunctionalComponent
} from '../../src/internal';

export { ComponentChildren } from '../..';

export { PreactElement } from '../../src/internal';

export interface Component<P = {}, S = {}> extends PreactComponent<P, S> {
  isReactComponent: object;
  isPureReactComponent?: true;
}

export interface FunctionalComponent<P = {}> extends PreactFunctionalComponent<P> {
  shouldComponentUpdate?(nextProps: Readonly<P>): boolean;
  _forwarded?: true;
}

export interface VNode<T = any> extends PreactVNode<T> {
  $$typeof: symbol | string;
  preactCompatNormalized: boolean;
}

export interface ForwardFn<P = {}, T = any> {
  (props: P, ref: Ref<T>): VNode;
  displayName?: string;
}


import { enqueueRender } from './component';

export let i = 0;

/**
 *
 * @param {any} defaultValue
 */
export function createContext(defaultValue) {
	const ctx = {};

	const context = {
		_id: '__cC' + i++,
		_defaultValue: defaultValue,
		Consumer(props, context) {
			return props.children(context);
		},
		Provider(props) {
			if (!this.getChildContext) {
				const subs = [];
				this.getChildContext = () => {
					ctx[context._id] = this;
					return ctx;
				};
				this.shouldComponentUpdate = props => {
					subs.some(c => {
						// Check if still mounted
						if (c._parentDom) {
							c.context = props.value;
							enqueueRender(c);
						}
					});
				};
				this.sub = (c) => {
					subs.push(c);
					let old = c.componentWillUnmount;
					c.componentWillUnmount = () => {
						subs.splice(subs.indexOf(c), 1);
						old && old.call(c);
					};
				};
			}
			return props.children;
		}
	};

	context.Consumer.contextType = context;

	return context;
}


import * as preact from "./index";

export interface Options extends preact.Options {
	/** Attach a hook that is invoked before render, mainly to check the arguments. */
	_root?(vnode: preact.ComponentChild, parent: Element | Document | ShadowRoot | DocumentFragment): void;
	/** Attach a hook that is invoked before a vnode is diffed. */
	diff?(vnode: VNode): void;
	/** Attach a hook that is invoked after a tree was mounted or was updated. */
	_commit?(vnode: VNode): void;
	/** Attach a hook that is invoked before a vnode has rendered. */
	_render?(vnode: VNode): void;
	/** Attach a hook that is invoked before a hook's state is queried. */
	_hook?(component: Component): void;
	/** Attach a hook that is invoked after an error is caught in a component but before calling lifecycle hooks */
	catchError?(error: any, vnode: VNode): void;
	/**
	 * Attach a hook that is invoked after an error is caught while executing render.
	 *
	 * When this hook returns true, the diffing on the affected vnode will be stopped.
	 * When this hook returns false, the error will be thrown (and thus passed to catchError or lifecycle hooks)
	 *
	 * @param error The error caught
	 * @param vnode The VNode whose component's render method threw an error
	 * @return Return a boolean indicating whether the error was handled by the hook or not
	 */
	catchRender?(error: any, vnode: VNode): boolean;
}

export interface FunctionalComponent<P = {}> extends preact.FunctionComponent<P> {
	// Define getDerivedStateFromProps as undefined on FunctionalComponent
	// to get rid of some errors in `diff()`
	getDerivedStateFromProps?: undefined;
}

// Redefine ComponentFactory using our new internal FunctionalComponent interface above
export type ComponentFactory<P> = preact.ComponentClass<P> | FunctionalComponent<P>;

export interface PreactElement extends HTMLElement {
	_children?: VNode<any> | null;
	/** Event listeners to support event delegation */
	_listeners: Record<string, (e: Event) => void>;

	// Preact uses this attribute to detect SVG nodes
	ownerSVGElement?: SVGElement | null;

	// style: HTMLElement["style"]; // From HTMLElement

	data?: string | number; // From Text node
}

export interface VNode<P = {}> extends preact.VNode<P> {
	// Redefine type here using our internal ComponentFactory type
	type: string | ComponentFactory<P> | null;
	_children: Array<VNode> | null;
	_parent: VNode | null;
	_depth: number | null;
	/**
	 * The [first (for Fragments)] DOM child of a VNode
	 */
	_dom: PreactElement | Text | null;
	/**
	 * The last dom child of a Fragment, or components that return a Fragment
	 */
	_lastDomChild: PreactElement | Text | null;
	_component: Component | null;
}

export interface Component<P = {}, S = {}> extends preact.Component<P, S> {
	constructor: preact.ComponentType<P>;
	state: S; // Override Component["state"] to not be readonly for internal use, specifically Hooks
	base?: PreactElement;

	_dirty: boolean;
	_renderCallbacks: Array<() => void>;
	_context?: any;
	_vnode?: VNode<P> | null;
	_nextState?: S | null;
	/** Only used in the devtools to later dirty check if state has changed */
	_prevState?: S | null;
	/**
	 * Pointer to the parent dom node. This is only needed for top-level Fragment
	 * components or array returns.
	 */
	_parentDom?: PreactElement | null;
	_processingException?: Component<any, any> | null;
	_pendingError?: Component<any, any> | null;
}

export interface PreactContext extends preact.Context<any> {
	_id: string;
	_defaultValue: any;
}


keep keep keep keep replace keep keep keep keep

import { assign } from './util';
import { diff, commitRoot } from './diff/index';
import options from './options';
import { Fragment } from './create-element';

/**
 * Base Component class. Provides `setState()` and `forceUpdate()`, which
 * trigger rendering
 * @param {object} props The initial component props
 * @param {object} context The initial context from parent components'
 * getChildContext
 */
export function Component(props, context) {
	this.props = props;
	this.context = context;
	// this.constructor // When component is functional component, this is reset to functional component
	// if (this.state==null) this.state = {};
	// this.state = {};
	// this._dirty = true;
	// this._renderCallbacks = []; // Only class components

	// Other properties that Component will have set later,
	// shown here as commented out for quick reference
	// this.base = null;
	// this._context = null;
	// this._vnode = null;
	// this._nextState = null; // Only class components
	// this._processingException = null; // Always read, set only when handling error
	// this._pendingError = null; // Always read, set only when handling error. This is used to indicate at diffTime to set _processingException
}

/**
 * Update component state and schedule a re-render.
 * @param {object | ((s: object, p: object) => object)} update A hash of state
 * properties to update with new values or a function that given the current
 * state and props returns a new partial state
 * @param {() => void} [callback] A function to be called once component state is
 * updated
 */
Component.prototype.setState = function(update, callback) {
	// only clone state when copying to nextState the first time.
	let s = (this._nextState!==this.state && this._nextState) || (this._nextState = assign({}, this.state));

	// if update() mutates state in-place, skip the copy:
	if (typeof update!=='function' || (update = update(s, this.props))) {
		assign(s, update);
	}

	// Skip update if updater function returned null
	if (update==null) return;

	if (this._vnode) {
		if (callback) this._renderCallbacks.push(callback);
		enqueueRender(this);
	}
};

/**
 * Immediately perform a synchronous re-render of the component
 * @param {() => void} [callback] A function to be called after component is
 * re-renderd
 */
Component.prototype.forceUpdate = function(callback) {
	let vnode = this._vnode, oldDom = this._vnode._dom, parentDom = this._parentDom;
	if (parentDom) {
		// Set render mode so that we can differantiate where the render request
		// is coming from. We need this because forceUpdate should never call
		// shouldComponentUpdate
		const force = callback!==false;

		let mounts = [];
		let newDom = diff(parentDom, vnode, assign({}, vnode), this._context, parentDom.ownerSVGElement!==undefined, null, mounts, force, oldDom == null ? getDomSibling(vnode) : oldDom);
		commitRoot(mounts, vnode);

		if (newDom != oldDom) {
			// Update parent component's _dom and c.base pointers
			// Note: parent component's _lastDomChild may become out of date, but that is okay,
			// because _lastDomChild isn't used to restart diffs. It is only important while a
			// diff for that component is happening and the diff will reset it to the correct value
			// if/when that component is updated
			while (
				vnode._parent
				&& vnode._parent._children.indexOf(vnode) == 0
				&& (vnode = vnode._parent)
				&& vnode._component
				&& vnode._dom == oldDom
			) {
				vnode._dom = vnode._component.base = newDom;
			}
		}
	}
	if (callback) callback();
};

/**
 * Accepts `props` and `state`, and returns a new Virtual DOM tree to build.
 * Virtual DOM is generally constructed via [JSX](http://jasonformat.com/wtf-is-jsx).
 * @param {object} props Props (eg: JSX attributes) received from parent
 * element/component
 * @param {object} state The component's current state
 * @param {object} context Context object, as returned by the nearest
 * ancestor's `getChildContext()`
 * @returns {import('./index').ComponentChildren | void}
 */
Component.prototype.render = Fragment;

/**
 * @param {import('./internal').VNode} vnode
 * @param {number | null} [childIndex]
 */
export function getDomSibling(vnode, childIndex) {
	if (childIndex == null) {
		// Use childIndex==null as a signal to resume the search from the vnode's sibling
		return vnode._parent
			? getDomSibling(vnode._parent, vnode._parent._children.indexOf(vnode) + 1)
			: null;
	}

	let sibling;
	for (; childIndex < vnode._children.length; childIndex++) {
		sibling = vnode._children[childIndex];

		if (sibling != null) {
			return typeof sibling.type !== 'function'
				? sibling._dom
				: getDomSibling(sibling, 0);
		}
	}

	// If we get here, we have not found a DOM node in this vnode's children.
	// We must resume from this vnode's sibling (in it's parent _children array)
	// Only climb up and search the parent if we aren't searching through a DOM
	// VNode (meaning we reached the DOM parent of the original vnode that began
	// the search)
	return typeof vnode.type === 'function' ? getDomSibling(vnode) : null;
}

/**
 * The render queue
 * @type {Array<import('./internal').Component>}
 */
let q = [];

/**
 * Asynchronously schedule a callback
 * @type {(cb) => void}
 */
const defer = typeof Promise=='function' ? Promise.prototype.then.bind(Promise.resolve()) : setTimeout;

/*
 * The value of `Component.debounce` must asynchronously invoke the passed in callback. It is
 * important that contributors to Preact can consistenly reason about what calls to `setState`, etc.
 * do, and when their effects will be applied. See the links below for some further reading on designing
 * asynchronous APIs.
 * * [Designing APIs for Asynchrony](https://blog.izs.me/2013/08/designing-apis-for-asynchrony)
 * * [Callbacks synchronous and asynchronous](https://blog.ometer.com/2011/07/24/callbacks-synchronous-and-asynchronous/)
 */

/**
 * Enqueue a rerender of a component
 * @param {import('./internal').Component} c The component to rerender
 */
export function enqueueRender(c) {
	if (!c._dirty && (c._dirty = true) && q.push(c) === 1) {
		(options.debounceRendering || defer)(process);
	}
}

/** Flush the render queue by rerendering all queued components */
function process() {
	let p;
	q.sort((a, b) => b._vnode._depth - a._vnode._depth);
	while ((p=q.pop())) {
		// forceUpdate's callback argument is reused here to indicate a non-forced update.
		if (p._dirty) p.forceUpdate(false);
	}
}


import { options } from 'preact';

/**
 * Setup a rerender function that will drain the queue of pending renders
 * @returns {() => void}
 */
export function setupRerender() {
	options.__test__previousDebounce = options.debounceRendering;
	options.debounceRendering = cb => options.__test__drainQueue = cb;
	return () => options.__test__drainQueue && options.__test__drainQueue();
}

/**
 * Run a test function, and flush all effects and rerenders after invoking it
 * @param {() => void} cb The function under test
 */
export function act(cb) {
	const previousRequestAnimationFrame = options.requestAnimationFrame;
	const rerender = setupRerender();

	/** @type {() => void} */
	let flush, toFlush;

	// Override requestAnimationFrame so we can flush pending hooks.
	options.requestAnimationFrame = (fc) => flush = fc;

	// Execute the callback we were passed.
	cb();
	rerender();

	while (flush) {
		toFlush = flush;
		flush = null;

		toFlush();
		rerender();
	}

	teardown();
	options.requestAnimationFrame = previousRequestAnimationFrame;
}

/**
 * Teardown test environment and reset preact's internal state
 */
export function teardown() {
	if (options.__test__drainQueue) {
		// Flush any pending updates leftover by test
		options.__test__drainQueue();
		delete options.__test__drainQueue;
	}

	if (typeof options.__test__previousDebounce !== 'undefined') {
		options.debounceRendering = options.__test__previousDebounce;
		delete options.__test__previousDebounce;
	}
	else {
		options.debounceRendering = undefined;
	}
}


import { h, render, options } from 'preact';
import { setupScratch, teardown } from '../../../test/_util/helpers';
import { useDebugValue, useState } from '../../src';

/** @jsx h */

describe('useDebugValue', () => {

	/** @type {HTMLDivElement} */
	let scratch;

	beforeEach(() => {
		scratch = setupScratch();
	});

	afterEach(() => {
		teardown(scratch);
		delete options.useDebugValue;
	});

	it('should do nothing when no options hook is present', () => {
		function useFoo() {
			useDebugValue('foo');
			return useState(0);
		}

		function App() {
			let [v] = useFoo();
			return
{v}
;
		}

		expect(() => render(<App />, scratch)).to.not.throw();
	});

	it('should call options hook with value', () => {
		let spy = options.useDebugValue = sinon.spy();

		function useFoo() {
			useDebugValue('foo');
			return useState(0);
		}

		function App() {
			let [v] = useFoo();
			return
{v}
;
		}

		render(<App />, scratch);

		expect(spy).to.be.calledOnce;
		expect(spy).to.be.calledWith('foo');
	});

	it('should apply optional formatter', () => {
		let spy = options.useDebugValue = sinon.spy();

		function useFoo() {
			useDebugValue('foo', x => x + 'bar');
			return useState(0);
		}

		function App() {
			let [v] = useFoo();
			return
{v}
;
		}

		render(<App />, scratch);

		expect(spy).to.be.calledOnce;
		expect(spy).to.be.calledWith('foobar');
	});
});


export function scheduleEffectAssert(assertFn) {
	return new Promise(resolve => {
		requestAnimationFrame(() =>
			setTimeout(() => {
				assertFn();
				resolve();
			}, 0)
		);
	});
}


>(opts: FastifyHttp2SecureOptions<Server, Logger>): FastifyInstance<Server, Request, Reply, Logger> & PromiseLike<FastifyInstance<Server, Request, Reply, Logger>>

import * as http from 'http'
import * as http2 from 'http2'
import * as https from 'https'
import * as LightMyRequest from 'light-my-request'

import { FastifyRequest, RequestGenericInterface } from './types/request'
import { RawServerBase, RawServerDefault, RawRequestDefaultExpression, RawReplyDefaultExpression } from './types/utils'
import { FastifyLoggerInstance, FastifyLoggerOptions } from './types/logger'
import { FastifyInstance } from './types/instance'
import { FastifyServerFactory } from './types/serverFactory'
import * as ajv from 'ajv'
import { FastifyError } from 'fastify-error'
import { FastifyReply } from './types/reply'

/**
 * Fastify factor function for the standard fastify http, https, or http2 server instance.
 *
 * The default function utilizes http
 *
 * @param opts Fastify server options
 * @returns Fastify server instance
 */
declare function fastify<
  Server extends http2.Http2SecureServer,
  Request extends RawRequestDefaultExpression<Server> = RawRequestDefaultExpression<Server>,
  Reply extends RawReplyDefaultExpression<Server> = RawReplyDefaultExpression<Server>,
  Logger extends FastifyLoggerInstance = FastifyLoggerInstance
>(opts: FastifyHttp2SecureOptions<Server, Logger>): FastifyInstance<Server, Request, Reply, Logger>
declare function fastify<
  Server extends http2.Http2Server,
  Request extends RawRequestDefaultExpression<Server> = RawRequestDefaultExpression<Server>,
  Reply extends RawReplyDefaultExpression<Server> = RawReplyDefaultExpression<Server>,
  Logger extends FastifyLoggerInstance = FastifyLoggerInstance
>(opts: FastifyHttp2Options<Server, Logger>): FastifyInstance<Server, Request, Reply, Logger>
declare function fastify<
  Server extends https.Server,
  Request extends RawRequestDefaultExpression<Server> = RawRequestDefaultExpression<Server>,
  Reply extends RawReplyDefaultExpression<Server> = RawReplyDefaultExpression<Server>,
  Logger extends FastifyLoggerInstance = FastifyLoggerInstance
>(opts: FastifyHttpsOptions<Server, Logger>): FastifyInstance<Server, Request, Reply, Logger>
declare function fastify<
  Server extends http.Server,
  Request extends RawRequestDefaultExpression<Server> = RawRequestDefaultExpression<Server>,
  Reply extends RawReplyDefaultExpression<Server> = RawReplyDefaultExpression<Server>,
  Logger extends FastifyLoggerInstance = FastifyLoggerInstance
>(opts?: FastifyServerOptions<Server, Logger>): FastifyInstance<Server, Request, Reply, Logger>
export default fastify

type FastifyHttp2SecureOptions<
  Server extends http2.Http2SecureServer,
  Logger extends FastifyLoggerInstance = FastifyLoggerInstance
> = FastifyServerOptions<Server, Logger> & {
  http2: true,
  https: http2.SecureServerOptions
}

type FastifyHttp2Options<
  Server extends http2.Http2Server,
  Logger extends FastifyLoggerInstance = FastifyLoggerInstance
> = FastifyServerOptions<Server, Logger> & {
  http2: true,
  http2SessionTimeout?: number,
}

type FastifyHttpsOptions<
  Server extends https.Server,
  Logger extends FastifyLoggerInstance = FastifyLoggerInstance
> = FastifyServerOptions<Server, Logger> & {
  https: https.ServerOptions
}
/**
 * Options for a fastify server instance. Utilizes conditional logic on the generic server parameter to enforce certain https and http2
 */
export type FastifyServerOptions<
  RawServer extends RawServerBase = RawServerDefault,
  Logger extends FastifyLoggerInstance = FastifyLoggerInstance
> = {
  ignoreTrailingSlash?: boolean,
  connectionTimeout?: number,
  keepAliveTimeout?: number,
  pluginTimeout?: number,
  bodyLimit?: number,
  maxParamLength?: number,
  disableRequestLogging?: boolean,
  onProtoPoisoning?: 'error' | 'remove' | 'ignore',
  onConstructorPoisoning?: 'error' | 'remove' | 'ignore',
  logger?: boolean | FastifyLoggerOptions<RawServer> | Logger,
  serverFactory?: FastifyServerFactory<RawServer>,
  caseSensitive?: boolean,
  requestIdHeader?: string,
  requestIdLogLabel?: string;
  genReqId?: <RequestGeneric extends RequestGenericInterface = RequestGenericInterface>(req: FastifyRequest<RequestGeneric, RawServer, RawRequestDefaultExpression<RawServer>>) => string,
  trustProxy?: boolean | string | string[] | number | TrustProxyFunction,
  querystringParser?: (str: string) => { [key: string]: string | string[] },
  versioning?: {
    storage(): {
      get(version: string): string | null,
      set(version: string, store: Function): void
      del(version: string): void,
      empty(): void
    },
    deriveVersion<Context>(req: Object, ctx?: Context): string // not a fan of using Object here. Also what is Context? Can either of these be better defined?
  },
  return503OnClosing?: boolean,
  ajv?: {
    customOptions?: ajv.Options,
    plugins?: Function[]
  },
  frameworkErrors?: <RequestGeneric extends RequestGenericInterface = RequestGenericInterface>(
    error: FastifyError,
    req: FastifyRequest<RequestGeneric, RawServer, RawRequestDefaultExpression<RawServer>>,
    res: FastifyReply<RawServer, RawRequestDefaultExpression<RawServer>, RawReplyDefaultExpression<RawServer>>
  ) => void,
  rewriteUrl?: (req: RawRequestDefaultExpression<RawServer>) => string
}

type TrustProxyFunction = (address: string, hop: number) => boolean

/* Export all additional types */
export { FastifyRequest, RequestGenericInterface } from './types/request'
export { FastifyReply } from './types/reply'
export { FastifyPluginCallback, FastifyPluginAsync, FastifyPluginOptions, FastifyPlugin } from './types/plugin'
export { FastifyInstance } from './types/instance'
export { FastifyLoggerOptions, FastifyLoggerInstance, FastifyLogFn, LogLevel } from './types/logger'
export { FastifyContext } from './types/context'
export { RouteHandler, RouteHandlerMethod, RouteOptions, RouteShorthandMethod, RouteShorthandOptions, RouteShorthandOptionsWithHandler } from './types/route'
export * from './types/register'
export { FastifyBodyParser, FastifyContentTypeParser, AddContentTypeParser, hasContentTypeParser } from './types/content-type-parser'
export { FastifyError, ValidationResult } from 'fastify-error'
export { FastifySchema, FastifySchemaCompiler } from './types/schema'
export { HTTPMethods, RawServerBase, RawRequestDefaultExpression, RawReplyDefaultExpression, RawServerDefault, ContextConfigDefault, RequestBodyDefault, RequestQuerystringDefault, RequestParamsDefault, RequestHeadersDefault } from './types/utils'
export * from './types/hooks'
export { FastifyServerFactory, FastifyServerFactoryHandler } from './types/serverFactory'
export { fastify }


Fastify

## Lifecycle
Following the schema of the internal lifecycle of Fastify.

On the right branch of every section there is the next phase of the lifecycle, on the left branch there is the corresponding error code that will be generated if the parent throws an error *(note that all the errors are automatically handled by Fastify)*.
```
Incoming Request
  │
  └─▶ Routing
        │
        └─▶ Instance Logger
             │
       404 ◀─┴─▶ onRequest Hook
                  │
        4**/5** ◀─┴─▶ preParsing Hook
                        │
              4**/5** ◀─┴─▶ Parsing
                             │
                   4**/5** ◀─┴─▶ preValidation Hook
                                  │
                            415 ◀─┴─▶ Validation
                                        │
                                  400 ◀─┴─▶ preHandler Hook
                                              │
                                    4**/5** ◀─┴─▶ User Handler
                                                    │
                                                    └─▶ Reply
                                                          │
                                                4**/5** ◀─┴─▶ preSerialization Hook
                                                                │
                                                                └─▶ onSend Hook
                                                                      │
                                                            4**/5** ◀─┴─▶ Outgoing Response
                                                                            │
                                                                            └─▶ onResponse Hook
```


'use strict'

const { connect } = require('net')
const t = require('tap')
const test = t.test
const Fastify = require('..')
const { kRequest } = require('../lib/symbols.js')

test('default 400 on request error', t => {
  t.plan(4)

  const fastify = Fastify()

  fastify.post('/', function (req, reply) {
    reply.send({ hello: 'world' })
  })

  fastify.inject({
    method: 'POST',
    url: '/',
    simulate: {
      error: true
    },
    body: {
      text: '12345678901234567890123456789012345678901234567890'
    }
  }, (err, res) => {
    t.error(err)
    t.strictEqual(res.statusCode, 400)
    t.strictEqual(res.headers['content-type'], 'application/json; charset=utf-8')
    t.deepEqual(JSON.parse(res.payload), {
      error: 'Bad Request',
      message: 'Simulated',
      statusCode: 400
    })
  })
})

test('default 400 on request error with custom error handler', t => {
  t.plan(6)

  const fastify = Fastify()

  fastify.setErrorHandler(function (err, request, reply) {
    t.type(request, 'object')
    t.type(request, fastify[kRequest])
    reply
      .code(err.statusCode)
      .type('application/json; charset=utf-8')
      .send(err)
  })

  fastify.post('/', function (req, reply) {
    reply.send({ hello: 'world' })
  })

  fastify.inject({
    method: 'POST',
    url: '/',
    simulate: {
      error: true
    },
    body: {
      text: '12345678901234567890123456789012345678901234567890'
    }
  }, (err, res) => {
    t.error(err)
    t.strictEqual(res.statusCode, 400)
    t.strictEqual(res.headers['content-type'], 'application/json; charset=utf-8')
    t.deepEqual(JSON.parse(res.payload), {
      error: 'Bad Request',
      message: 'Simulated',
      statusCode: 400
    })
  })
})

test('default clientError handler ignores ECONNRESET', t => {
  t.plan(3)

  let logs = ''
  let response = ''

  const fastify = Fastify({
    bodyLimit: 1,
    keepAliveTimeout: 100,
    logger: {
      level: 'trace',
      stream: {
        write () {
          logs += JSON.stringify(arguments)
        }
      }
    }
  })

  fastify.get('/', (request, reply) => {
    reply.send('OK')

    process.nextTick(() => {
      const error = new Error()
      error.code = 'ECONNRESET'

      fastify.server.emit('clientError', error, request.raw.socket)
    })
  })

  fastify.listen(0, function (err) {
    t.error(err)
    fastify.server.unref()

    const client = connect(fastify.server.address().port)

    client.on('data', chunk => {
      response += chunk.toString('utf-8')
    })

    client.on('end', () => {
      t.match(response, /^HTTP\/1.1 200 OK/)
      t.notMatch(logs, /ECONNRESET/)
    })

    client.resume()
    client.write('GET / HTTP/1.1\r\n')
    client.write('Connection: close\r\n')
    client.write('\r\n\r\n')
  })
})


# Fastify is an OPEN Open Source Project

## What?

Individuals making significant and valuable contributions are given commit-access to the project to contribute as they see fit. This project is more like an open wiki than a standard guarded open source project.

## Rules

There are a few basic ground-rules for contributors:

1. **No `--force` pushes** on `master` or modifying the Git history in any way after a PR has been merged.
1. **Non-master branches** ought to be used for ongoing work.
1. **External API changes and significant modifications** ought to be subject to an **internal pull-request** to solicit feedback from other contributors.
1. Internal pull-requests to solicit feedback are *encouraged* for any other non-trivial contribution but left to the discretion of the contributor.
1. Contributors should attempt to adhere to the prevailing code-style.
1. At least two contributors, or one core member, must approve pull-requests prior to merging.
1. All integrated CI services must be green before a pull-request can be merged.
1. SemVer-major changes in this repository must be merged by a lead maintainer.
1. In case it is not possible to reach consensus in a pull-request, the decision is left to the lead maintainers team.

### Fastify v1.x

Code for Fastify's **v1.x** is in [branch 1.x](https://github.com/fastify/fastify/tree/1.x), so all Fastify 1.x related changes should be based on **`branch 1.x`**.

### Fastify v2.x

Code for Fastify's **v2.x** is in [branch 2.x](https://github.com/fastify/fastify/tree/2.x), so all Fastify 2.x related changes should be based on **`branch 2.x`**.

## Releases

Declaring formal releases remains the prerogative of the lead maintainers. Do not bump version numbers in pull requests.

## Plugins

The contributors to the Fastify's plugins must attend the same rules of the Fastify repository with few adjustements:

1. A release can be published by any member.
1. The plugin version must follow the [semver](http://semver.org/) specification.
1. The Node.js compatibility must match with the Fastify's master branch.
1. The new release must have the changelog information stored in the GitHub release.
     For this scope we suggest to adopt a tool like [`releasify`](https://github.com/fastify/releasify) to archive this.
1. PR opened by bots (like Greenkeeper) can be merged if the CI is green and the Node.js versions supported are the same of the plugin.

## Changes to this arrangement

This is an experiment and feedback is welcome! This document may also be subject to pull-requests or changes by contributors where you believe you have something valuable to add or change.

# Fastify Organization Structure

We have many collaborators, and every kind of contribution is welcome! Our organization is structured as follows:

|  Team | Responsibility  |  Repository |
|---|---|---|
| `@fastify/leads` | Fastify Lead Maintainers | GitHub organization owners |
| `@fastify/core`   |  Fastify Core development  |  `fastify`, `fast-json-stringify`, `light-my-request`, `fastify-plugin`, `middie` |
| `@fastify/plugins`   |  Build, maintain and release Fastify plugins  |  All plugins repositories |
| `@fastify/benchmarks`   |  Build and maintain our benchmarks suite  |  `benchmarks` |
| `@fastify/docs-chinese`   |  Translate the Fastify documentation in Chinese  |  `docs-chinese` |

Every memeber of the org is also part of `@fastify/fastify`.

### Onboarding Collaborators

Welcome to the team! We are happy to have you. Before you start, please complete the following tasks:
1. Set up 2 factor authentication for GitHub and NPM
  - [GitHub 2FA](https://help.github.com/en/articles/securing-your-account-with-two-factor-authentication-2fa)
  - [NPM 2FA](https://docs.npmjs.com/about-two-factor-authentication)
2. Choose which team to join *(more than one is ok!)* based on how you want to help.
3. Open a pull request to [`fastify/fastify:master`](https://github.com/fastify/fastify/pulls) that adds your name, username, and email to the team you have choosen in the [README.md](./README.md) and [package.json](./package.json) *(if you are part of the core team)* files. The members lists are sorted alphabetically; make sure to add your name in the proper order.
4. Open a pull request to [`fastify/website:master`](https://github.com/fastify/website/pulls) adding yourself to the [team.yml](https://github.com/fastify/website/blob/master/src/website/data/team.yml) file. This list is also sorted alphabetically so make sure to add your name in the proper order. Use your GitHub profile icon for the `picture:` field.
5. The person that does the onboarding must add you to the [npm org](https://www.npmjs.com/org/fastify), so that you can help maintaining the official plugins.

### Offboarding Collaborators

We are thankful to you and we are really glad to have worked with you.
We'll be really happy to see you here again if you want to come back, but for now the person that did the onboarding must:
1. Ask the collaborator if they want to stay or not.
1. If the collaborator can't work with us anymore, they should:
  1. Open a pull request to [`fastify/fastify:master`](https://github.com/fastify/fastify/pulls) and move themselves in the *Past Collaborators* section.
  2. Open a pull request to [`fastify/website:master`](https://github.com/fastify/website/pulls) and move themselves in the *Past Collaborators* section in the [team.yml](https://github.com/fastify/website/blob/master/src/website/data/team.yml) file.

The person that did the onboarding must:
1. If the collaborator doesn't reply to the ping in reasonable time, open the pull requests described above.
2. Remove the collaborator from the Fastify teams on GitHub.
3. Remove the collaborator from the [npm org](https://www.npmjs.com/org/fastify).
4. Remove the collaborator from the Azure team.
-----------------------------------------

## Developer's Certificate of Origin 1.1

By making a contribution to this project, I certify that:

* (a) The contribution was created in whole or in part by me and I
  have the right to submit it under the open source license
  indicated in the file; or

* (b) The contribution is based upon previous work that, to the best
  of my knowledge, is covered under an appropriate open source
  license and I have the right under that license to submit that
  work with modifications, whether created in whole or in part
  by me, under the same open source license (unless I am
  permitted to submit under a different license), as indicated
  in the file; or

* (c) The contribution was provided directly to me by some other
  person who certified (a), (b) or (c) and I have not modified
  it.

* (d) I understand and agree that this project and the contribution
  are public and that a record of the contribution (including all
  personal information I submit with it, including my sign-off) is
  maintained indefinitely and may be redistributed consistent with
  this project or the open source license(s) involved.


>(opts: FastifyHttp2Options<Server, Logger>): FastifyInstance<Server, Request, Reply, Logger> & PromiseLike<FastifyInstance<Server, Request, Reply, Logger>>


Fastify

## Request
The first parameter of the handler function is `Request`.

Request is a core Fastify object containing the following fields:
- `query` - the parsed querystring
- `body` - the body
- `params` - the params matching the URL
- `headers` - the headers
- `raw` - the incoming HTTP request from Node core
- `req` *(deprecated, use `.raw` instead)* - the incoming HTTP request from Node core
- `id` - the request id
- `log` - the logger instance of the incoming request
- `ip` - the IP address of the incoming request
- `ips` - an array of the IP addresses in the `X-Forwarded-For` header of the incoming request (only when the [`trustProxy`](./Server.md#factory-trust-proxy) option is enabled)
- `hostname` - the hostname of the incoming request
- `method` - the method of the incoming request
- `url` - the url of the incoming request
- `connection` - the underlying connection of the incoming request

```js
fastify.post('/:params', options, function (request, reply) {
  console.log(request.body)
  console.log(request.query)
  console.log(request.params)
  console.log(request.headers)
  console.log(request.raw)
  console.log(request.id)
  console.log(request.ip)
  console.log(request.ips)
  console.log(request.hostname)
  request.log.info('some info')
})
```


'use strict'

const t = require('tap')
const test = t.test
const sget = require('simple-get').concat
const fastify = require('..')()

const opts = {
  schema: {
    response: {
      '2xx': {
        type: 'object',
        properties: {
          hello: {
            type: 'string'
          }
        }
      }
    }
  }
}

fastify.get('/return', opts, function (req, reply) {
  const promise = new Promise((resolve, reject) => {
    resolve({ hello: 'world' })
  })
  return promise
})

fastify.get('/return-error', opts, function (req, reply) {
  const promise = new Promise((resolve, reject) => {
    reject(new Error('some error'))
  })
  return promise
})

fastify.get('/double', function (req, reply) {
  setTimeout(function () {
    // this should not throw
    reply.send({ hello: 'world' })
  }, 20)
  return Promise.resolve({ hello: '42' })
})

fastify.get('/thenable', opts, function (req, reply) {
  setImmediate(function () {
    reply.send({ hello: 'world' })
  })
  return reply
})

fastify.get('/thenable-error', opts, function (req, reply) {
  setImmediate(function () {
    reply.send(new Error('kaboom'))
  })
  return reply
})

fastify.get('/return-reply', opts, function (req, reply) {
  return reply.send({ hello: 'world' })
})

fastify.listen(0, err => {
  t.error(err)
  fastify.server.unref()

  test('shorthand - sget return promise es6 get', t => {
    t.plan(4)
    sget({
      method: 'GET',
      url: 'http://localhost:' + fastify.server.address().port + '/return'
    }, (err, response, body) => {
      t.error(err)
      t.strictEqual(response.statusCode, 200)
      t.strictEqual(response.headers['content-length'], '' + body.length)
      t.deepEqual(JSON.parse(body), { hello: 'world' })
    })
  })

  test('shorthand - sget promise es6 get return error', t => {
    t.plan(2)
    sget({
      method: 'GET',
      url: 'http://localhost:' + fastify.server.address().port + '/return-error'
    }, (err, response, body) => {
      t.error(err)
      t.strictEqual(response.statusCode, 500)
    })
  })

  test('sget promise double send', t => {
    t.plan(3)

    sget({
      method: 'GET',
      url: 'http://localhost:' + fastify.server.address().port + '/double'
    }, (err, response, body) => {
      t.error(err)
      t.strictEqual(response.statusCode, 200)
      t.deepEqual(JSON.parse(body), { hello: '42' })
    })
  })

  test('thenable', t => {
    t.plan(4)
    sget({
      method: 'GET',
      url: 'http://localhost:' + fastify.server.address().port + '/thenable'
    }, (err, response, body) => {
      t.error(err)
      t.strictEqual(response.statusCode, 200)
      t.strictEqual(response.headers['content-length'], '' + body.length)
      t.deepEqual(JSON.parse(body), { hello: 'world' })
    })
  })

  test('thenable (error)', t => {
    t.plan(2)
    sget({
      method: 'GET',
      url: 'http://localhost:' + fastify.server.address().port + '/thenable-error'
    }, (err, response, body) => {
      t.error(err)
      t.strictEqual(response.statusCode, 500)
    })
  })

  test('return-reply', t => {
    t.plan(4)
    sget({
      method: 'GET',
      url: 'http://localhost:' + fastify.server.address().port + '/return-reply'
    }, (err, response, body) => {
      t.error(err)
      t.strictEqual(response.statusCode, 200)
      t.strictEqual(response.headers['content-length'], '' + body.length)
      t.deepEqual(JSON.parse(body), { hello: 'world' })
    })
  })
})


'use strict'

const { test } = require('tap')
const Fastify = require('..')
const {
  FST_ERR_DEC_ALREADY_PRESENT,
  FST_ERR_MISSING_MIDDLEWARE
} = require('../lib/errors')

test('Should throw if the basic use API has not been overridden', t => {
  t.plan(1)
  const fastify = Fastify()

  try {
    fastify.use()
    t.fail('Should throw')
  } catch (err) {
    t.ok(err instanceof FST_ERR_MISSING_MIDDLEWARE)
  }
})

test('Should be able to override the default use API', t => {
  t.plan(1)
  const fastify = Fastify()
  fastify.decorate('use', () => true)
  t.strictEqual(fastify.use(), true)
})

test('Cannot decorate use twice', t => {
  t.plan(1)
  const fastify = Fastify()
  fastify.decorate('use', () => true)
  try {
    fastify.decorate('use', () => true)
  } catch (err) {
    t.ok(err instanceof FST_ERR_DEC_ALREADY_PRESENT)
  }
})

test('Encapsulation works', t => {
  t.plan(2)
  const fastify = Fastify()

  fastify.register((instance, opts, next) => {
    instance.decorate('use', () => true)
    t.strictEqual(instance.use(), true)
    next()
  })

  fastify.register((instance, opts, next) => {
    try {
      instance.use()
      t.fail('Should throw')
    } catch (err) {
      t.ok(err instanceof FST_ERR_MISSING_MIDDLEWARE)
    }
    next()
  })

  fastify.ready()
})


>(opts: FastifyHttpsOptions<Server, Logger>): FastifyInstance<Server, Request, Reply, Logger> & PromiseLike<FastifyInstance<Server, Request, Reply, Logger>>

import fastify, { FastifyInstance, FastifyServerOptions } from '../../fastify'
import * as http from 'http'
import * as https from 'https'
import * as http2 from 'http2'
import { Chain as LightMyRequestChain } from 'light-my-request';
import { expectType, expectError, expectAssignable } from 'tsd'
import { FastifyLoggerInstance, FastifyLoggerOptions } from '../../types/logger';

// FastifyInstance
// http server
expectType<FastifyInstance<http.Server, http.IncomingMessage, http.ServerResponse>>(fastify())
expectType<FastifyInstance<http.Server, http.IncomingMessage, http.ServerResponse>>(fastify({}))
// https server
expectType<FastifyInstance<https.Server, http.IncomingMessage, http.ServerResponse>>(fastify({ https: {} }))
// http2 server
expectType<FastifyInstance<http2.Http2Server, http2.Http2ServerRequest, http2.Http2ServerResponse>>(fastify({ http2: true, http2SessionTimeout: 1000 }))
expectType<FastifyInstance<http2.Http2SecureServer, http2.Http2ServerRequest, http2.Http2ServerResponse>>(fastify({ http2: true, https: {} }))
expectType<LightMyRequestChain>(fastify({ http2: true, https: {} }).inject())

expectError(fastify<http2.Http2Server>({ http2: false })) // http2 option must be true
expectError(fastify<http2.Http2SecureServer>({ http2: false })) // http2 option must be true

// server options
expectAssignable<FastifyInstance>(fastify({ http2: true }))
expectAssignable<FastifyInstance>(fastify({ ignoreTrailingSlash: true }))
expectAssignable<FastifyInstance>(fastify({ connectionTimeout: 1000 }))
expectAssignable<FastifyInstance>(fastify({ keepAliveTimeout: 1000 }))
expectAssignable<FastifyInstance>(fastify({ pluginTimeout: 1000 }))
expectAssignable<FastifyInstance>(fastify({ bodyLimit: 100 }))
expectAssignable<FastifyInstance>(fastify({ maxParamLength: 100 }))
expectAssignable<FastifyInstance>(fastify({ disableRequestLogging: true }))
expectAssignable<FastifyInstance>(fastify({ requestIdLogLabel: 'request-id' }))
expectAssignable<FastifyInstance>(fastify({ onProtoPoisoning: 'error' }))
expectAssignable<FastifyInstance>(fastify({ onConstructorPoisoning: 'error' }))
expectAssignable<FastifyInstance<http.Server, http.IncomingMessage, http.ServerResponse>>(fastify({ logger: true }))
expectAssignable<FastifyInstance<http.Server, http.IncomingMessage, http.ServerResponse, FastifyLoggerInstance>>(fastify({ logger: true }))
expectAssignable<FastifyInstance<http.Server, http.IncomingMessage, http.ServerResponse, FastifyLoggerInstance>>(fastify({
  logger: {
    level: 'info',
    genReqId: () => 'request-id',
    serializers: {
      req: () => {
        return {
          method: 'GET',
          url: '/',
          version: '1.0.0',
          hostname: 'localhost',
          remoteAddress: '127.0.0.1',
          remotePort: 3000
        }
      },
      res: () => {
        return {
          statusCode: 200
        }
      },
      err: () => {
        return {
          type: 'Error',
          message: 'foo',
          stack: ''
        }
      },
    }
  }
}))
const customLogger = {
  info: () => { },
  warn: () => { },
  error: () => { },
  fatal: () => { },
  trace: () => { },
  debug: () => { },
  child: () => customLogger
}
expectAssignable<FastifyInstance<http.Server, http.IncomingMessage, http.ServerResponse, FastifyLoggerInstance>>(fastify({ logger: customLogger }))
expectAssignable<FastifyInstance>(fastify({ serverFactory: () => http.createServer() }))
expectAssignable<FastifyInstance>(fastify({ caseSensitive: true }))
expectAssignable<FastifyInstance>(fastify({ requestIdHeader: 'request-id' }))
expectAssignable<FastifyInstance>(fastify({ genReqId: () => 'request-id' }))
expectAssignable<FastifyInstance>(fastify({ trustProxy: true }))
expectAssignable<FastifyInstance>(fastify({ querystringParser: () => ({ foo: 'bar' }) }))
expectAssignable<FastifyInstance>(fastify({
  versioning: {
    storage: () => ({
      get: () => 'foo',
      set: () => { },
      del: () => { },
      empty: () => { }
    }),
    deriveVersion: () => 'foo'
  }
}))
expectAssignable<FastifyInstance>(fastify({ return503OnClosing: true }))
expectAssignable<FastifyInstance>(fastify({
  ajv: {
    customOptions: {
      nullable: false
    },
    plugins: [() => { }]
  }
}))
expectAssignable<FastifyInstance>(fastify({ frameworkErrors: () => { } }))
expectAssignable<FastifyInstance>(fastify({
  rewriteUrl: (req) => req.url === '/hi' ? '/hello' : req.url!
}))


Fastify

## Errors

### Error Handling

Uncaught errors are likely to cause memory leaks, file descriptor leaks and other major production issues. [Domains](https://nodejs.org/en/docs/guides/domain-postmortem/) were introduced to try fixing this issue, but they did not. Given the fact that it is not possible to process all uncaught errors sensibly, the best way to deal with them at the moment is to [crash](https://nodejs.org/api/process.html#process_warning_using_uncaughtexception_correctly). In case of promises, make sure to [handle](https://nodejs.org/dist/latest-v8.x/docs/api/deprecations.html#deprecations_dep0018_unhandled_promise_rejections) errors [correctly](https://github.com/mcollina/make-promises-safe).

Fastify follows an all-or-nothing approach and aims to be lean and optimal as much as possible. Thus, the developer is responsible for making sure that the errors are handled properly. Most of the errors are usually a result of unexpected input data, so we recommend specifying a [JSON.schema validation](./Validation-and-Serialization.md) for your input data.

Fastify tries to catch as many uncaught errors it can without hindering performance. This includes:

1. synchronous routes, e.g. `app.get('/', () => { throw new Error('kaboom') })`
2. `async` routes, e.g. `app.get('/', async () => { throw new Error('kaboom') })`

In those two cases, the error will safely be caught by the promise and routed to the default error handler of Fastify for a generic `Internal Server Error` response. For customizing this behaviour, you should use [`setErrorHandler`](./Server.md#seterrorhandler).

### Fastify Error Codes

#### FST_ERR_BAD_URL

The router received an invalid url.

#### FST_ERR_CTP_ALREADY_PRESENT

The parser for this content type was already registered.

#### FST_ERR_CTP_INVALID_TYPE

The `Content-Type` should be a string.

#### FST_ERR_CTP_EMPTY_TYPE

The content type cannot be an empty string.

#### FST_ERR_CTP_INVALID_HANDLER

An invalid handler was passed for the content type.

#### FST_ERR_CTP_INVALID_PARSE_TYPE

The provided parse type is not supported. Accepted values are `string` or `buffer`.

#### FST_ERR_CTP_BODY_TOO_LARGE

The request body is larger than the provided limit.

#### FST_ERR_CTP_INVALID_MEDIA_TYPE

The received media type is not supported (i.e. there is no suitable `Content-Type` parser for it).

#### FST_ERR_CTP_INVALID_CONTENT_LENGTH

Request body size did not match Content-Length.

#### FST_ERR_DEC_ALREADY_PRESENT

A decorator with the same name is already registered.

#### FST_ERR_DEC_MISSING_DEPENDENCY

The decorator cannot be registered due to a missing dependency.

#### FST_ERR_HOOK_INVALID_TYPE

The hook name must be a string.

#### FST_ERR_HOOK_INVALID_HANDLER

The hook callback must be a function.

#### FST_ERR_LOG_INVALID_DESTINATION

The logger accepts either a `'stream'` or a `'file'` as the destination.

#### FST_ERR_REP_ALREADY_SENT

A response was already sent.

#### FST_ERR_SEND_INSIDE_ONERR

You cannot use `send` inside the `onError` hook.

#### FST_ERR_REP_INVALID_PAYLOAD_TYPE

Reply payload can either be a `string` or a `Buffer`.

#### FST_ERR_SCH_MISSING_ID

The schema provided does not have `$id` property.

#### FST_ERR_SCH_ALREADY_PRESENT

A schema with the same `$id` already exists.

#### FST_ERR_SCH_VALIDATION_BUILD

The JSON schema provided for validation to a route is not valid.

#### FST_ERR_SCH_SERIALIZATION_BUILD

The JSON schema provided for serialization of a route response is not valid.

#### FST_ERR_PROMISE_NOT_FULLFILLED

A promise may not be fulfilled with 'undefined' when statusCode is not 204.

#### FST_ERR_SEND_UNDEFINED_ERR

Undefined error has occurred.


'use strict'

const t = require('tap')
const test = t.test
const Fastify = require('..')
const autocannon = require('autocannon')

test('Should return 503 while closing - pipelining', t => {
  const fastify = Fastify()

  fastify.get('/', (req, reply) => {
    fastify.close()
    reply.send({ hello: 'world' })
  })

  fastify.listen(0, err => {
    t.error(err)

    const instance = autocannon({
      url: 'http://localhost:' + fastify.server.address().port,
      pipelining: 1,
      connections: 1,
      amount: 10
    })

    const codes = [200, 503]
    instance.on('response', (client, statusCode) => {
      t.strictEqual(statusCode, codes.shift())
    })

    instance.on('done', () => {
      t.strictEqual(codes.length, 0)
      t.end('Done')
    })
    instance.on('reqError', () => {
      t.strictEqual(codes.shift(), undefined)
    })
    instance.on('error', err => t.fail(err))
  })
})

test('Should not return 503 while closing - pipelining - return503OnClosing', t => {
  const fastify = Fastify({
    return503OnClosing: false
  })

  fastify.get('/', (req, reply) => {
    fastify.close()
    reply.send({ hello: 'world' })
  })

  fastify.listen(0, err => {
    t.error(err)

    const instance = autocannon({
      url: 'http://localhost:' + fastify.server.address().port,
      pipelining: 1,
      connections: 1,
      amount: 10
    })

    const codes = [200, 200]
    instance.on('response', (client, statusCode) => {
      t.strictEqual(statusCode, codes.shift())
    })

    instance.on('done', () => {
      t.strictEqual(codes.length, 0)
      t.end('Done')
    })
    instance.on('reqError', () => {
      t.strictEqual(codes.shift(), undefined)
    })
    instance.on('error', err => t.fail(err))
  })
})


>(opts?: FastifyServerOptions<Server, Logger>): FastifyInstance<Server, Request, Reply, Logger> & PromiseLike<FastifyInstance<Server, Request, Reply, Logger>>