From 5e3e9130e6933a0567b5e874daaf051a8ea9e628 Mon Sep 17 00:00:00 2001
From: Alex Feyerke
Date: Mon, 11 May 2026 10:12:34 +0200
Subject: [PATCH 01/13] chore: add 11ty data and filters for future versioned
docs
---
docs/_data/versions.js | 18 +++++++++++
eleventy.config.js | 72 ++++++++++++++++++++++++++++++++++++++++++
2 files changed, 90 insertions(+)
create mode 100644 docs/_data/versions.js
diff --git a/docs/_data/versions.js b/docs/_data/versions.js
new file mode 100644
index 0000000000..44bb81bfa1
--- /dev/null
+++ b/docs/_data/versions.js
@@ -0,0 +1,18 @@
+/*
+
+Gives us globally available info about PouchDB versions:
+
+- versions.all
+- versions.stable
+
+*/
+
+module.exports = () => {
+ const versions = ["7.0.0", "8.0.0", "9.0.0"];
+ return {
+ all: versions,
+ stable: versions.sort((a, b) =>
+ a.localeCompare(b, undefined, { numeric: true })
+ ).at(-1)
+ };
+};
diff --git a/eleventy.config.js b/eleventy.config.js
index 27f5011fdf..ac3f2a1280 100644
--- a/eleventy.config.js
+++ b/eleventy.config.js
@@ -50,6 +50,52 @@ module.exports = eleventyConfig => {
return collectionApi.getFilteredByTag('guides').sort((a, b) => a.data.index - b.data.index);
});
+ // Used to filter a collection of guides by version
+ eleventyConfig.addFilter("byVersion", (collection, version) => {
+ const result = collection.filter(item => {
+ const targetVersion = version || item.data.versions.stable;
+ return item.data.version === targetVersion;
+ }
+ );
+ return result;
+ });
+
+ // Used to convert a versioned URL to a stable URL, e.g:
+ // from http://localhost:4000/version/8.0.0/api.html
+ // to http://localhost:4000/api.html/
+ // This is used for the sidebar navigation items, the version switcher,
+ // and the old version warning block, and helps to build links to the `root`
+ // stable docs when the source is a versioned doc.
+ // The `version` prop is the page prop from wherever this is called,
+ // it determines whether we’re displaying versioned or stable docs.
+ eleventyConfig.addFilter("makeURLForStable", (content, version) => {
+ // if version is undefined, we’re in the root, so displaying `stable` docs.
+ // This means we need to strip the `/versions/[versionNumber]` fragment
+ // out of the URL
+ if (!version) {
+ return content.replace(/^\/version\/[^/]*/, '');
+ }
+ return content;
+ });
+
+ // Transforms any doc URL into the same URL for any other version.
+ // from http://localhost:4000/version/8.0.0/api.html
+ // to http://localhost:4000/version/7.3.0/api.html
+ // You can also go from `stable` to versioned or vice versa.
+ // Used in the version switcher component
+ eleventyConfig.addFilter("transformDocURL", (content, targetVersion) => {
+ if (!targetVersion) {
+ return content;
+ }
+ if (content.includes('/version/')) {
+ // replace the version in a URL
+ return content.replace(/^\/version\/[^/]*/, `/version/${targetVersion}`);
+ } else {
+ // Prepend the version to the URL if it doesn‘t have one
+ return `/version/${targetVersion}${content}`;
+ }
+ });
+
eleventyConfig.addCollection('pages', collectionApi => {
// zero-indexed, but skip page 1, as it's served at /blog/
const pageCount = Math.ceil(collectionApi.getFilteredByTag('posts').length / 5) - 1;
@@ -72,6 +118,32 @@ module.exports = eleventyConfig => {
.sort((a, b) => b.date - a.date || b.inputPath.localeCompare(a.inputPath));
});
+ // Make a `stable` collection out of the most recent release files
+ // The stable release does not exist as actual files on disk, it is
+ // basically a copy of the versioned docs in
+ // _docs/versions/,
+ // where versions.stable is defined in `_/docs/data/versions.js`
+ eleventyConfig.addCollection("stable", (collectionApi) => {
+ const stable = collectionApi.getAll().filter(item => {
+ const filePath = item.data.page.filePathStem;
+ const version = filePath.split('/')[2];
+ return version === item.data.versions.stable;
+ }
+ );
+ return stable;
+ });
+
+ // Filter to compute a frontmatter boolean from a frontmatter
+ // boolean. No idea why this is necessary, and it’s only used
+ // in stable.liquid to make sure a boolean taken from another
+ // file’s frontmatter stays a boolean and doesn’t become a string
+ eleventyConfig.addFilter('boolean', function (content) {
+ if (content === "true" || content === true) {
+ return true;
+ }
+ return false;
+ });
+
eleventyConfig.addFilter('first_paragraph', function (content) {
const marker = '
';
const idx = content.indexOf(marker);
From 41e4ac60613325ecb3a356d3f0492b35faa2966e Mon Sep 17 00:00:00 2001
From: Alex Feyerke
Date: Mon, 11 May 2026 10:15:57 +0200
Subject: [PATCH 02/13] chore: delete root level docs and make multiple
versioned copies
---
docs/_data/versions.js | 2 +-
docs/api.html | 42 --
docs/{ => version/7.3.0}/adapters.md | 2 +-
docs/version/7.3.0/api.html | 42 ++
.../7.3.0}/api/active_tasks.html | 0
.../7.3.0}/api/batch_create.html | 0
.../7.3.0}/api/batch_fetch.html | 0
.../7.3.0}/api/bulk_get.html | 0
.../7.3.0}/api/changes.html | 0
.../7.3.0}/api/close_database.html | 0
.../7.3.0}/api/compaction.html | 0
.../7.3.0}/api/create_database.html | 0
.../7.3.0}/api/create_document.html | 0
.../7.3.0}/api/create_index.html | 0
.../7.3.0}/api/database_information.html | 0
.../7.3.0}/api/defaults.html | 0
.../7.3.0}/api/delete_attachment.html | 0
.../7.3.0}/api/delete_database.html | 0
.../7.3.0}/api/delete_document.html | 0
.../7.3.0}/api/delete_index.html | 0
.../7.3.0}/api/events.html | 0
.../7.3.0}/api/explain_index.html | 0
.../7.3.0}/api/fetch_document.html | 0
.../7.3.0}/api/get_attachment.html | 0
.../7.3.0}/api/list_indexes.html | 0
.../7.3.0}/api/overview.html | 0
.../7.3.0}/api/plugins.html | 0
.../7.3.0}/api/purge.html | 0
.../7.3.0}/api/query_database.html | 0
.../7.3.0}/api/query_index.html | 0
.../7.3.0}/api/replication.html | 0
.../7.3.0}/api/revisions_diff.html | 0
.../7.3.0}/api/save_attachment.html | 0
.../7.3.0}/api/sync.html | 0
.../7.3.0}/api/view_cleanup.html | 0
docs/{ => version/7.3.0}/custom.md | 2 +-
docs/{ => version/7.3.0}/download.md | 2 +-
docs/{ => version/7.3.0}/errors.md | 2 +-
docs/{ => version/7.3.0}/external.md | 2 +-
docs/{ => version/7.3.0}/faq.md | 2 +-
docs/{ => version/7.3.0}/getting-started.md | 2 +-
docs/{ => version/7.3.0}/guides/async-code.md | 0
.../{ => version/7.3.0}/guides/attachments.md | 0
.../7.3.0}/guides/bulk-operations.md | 0
docs/{ => version/7.3.0}/guides/changes.md | 0
.../7.3.0}/guides/compact-and-destroy.md | 0
docs/{ => version/7.3.0}/guides/conflicts.md | 0
docs/{ => version/7.3.0}/guides/databases.md | 0
docs/{ => version/7.3.0}/guides/documents.md | 0
docs/{ => version/7.3.0}/guides/guides.json | 0
docs/{ => version/7.3.0}/guides/index.md | 0
.../7.3.0}/guides/local-documents.md | 0
.../7.3.0}/guides/mango-queries.md | 0
docs/{ => version/7.3.0}/guides/queries.md | 0
.../{ => version/7.3.0}/guides/replication.md | 0
.../7.3.0}/guides/setup-couchdb.md | 0
.../7.3.0}/guides/setup-pouchdb.md | 0
.../7.3.0}/guides/updating-deleting.md | 0
docs/{ => version/7.3.0}/learn.md | 2 +-
docs/{ => version/7.3.0}/offline.md | 0
docs/{ => version/7.3.0}/users.md | 2 +-
docs/version/8.0.0/adapters.md | 220 +++++++
docs/version/8.0.0/api.html | 42 ++
docs/version/8.0.0/api/active_tasks.html | 45 ++
docs/version/8.0.0/api/batch_create.html | 271 +++++++++
docs/version/8.0.0/api/batch_fetch.html | 205 +++++++
docs/version/8.0.0/api/bulk_get.html | 118 ++++
docs/version/8.0.0/api/changes.html | 358 ++++++++++++
docs/version/8.0.0/api/close_database.html | 33 ++
docs/version/8.0.0/api/compaction.html | 49 ++
docs/version/8.0.0/api/create_database.html | 64 +++
docs/version/8.0.0/api/create_document.html | 174 ++++++
docs/version/8.0.0/api/create_index.html | 302 ++++++++++
.../8.0.0/api/database_information.html | 59 ++
docs/version/8.0.0/api/defaults.html | 42 ++
docs/version/8.0.0/api/delete_attachment.html | 52 ++
docs/version/8.0.0/api/delete_database.html | 50 ++
docs/version/8.0.0/api/delete_document.html | 140 +++++
docs/version/8.0.0/api/delete_index.html | 129 +++++
docs/version/8.0.0/api/events.html | 14 +
docs/version/8.0.0/api/explain_index.html | 135 +++++
docs/version/8.0.0/api/fetch_document.html | 64 +++
docs/version/8.0.0/api/get_attachment.html | 116 ++++
docs/version/8.0.0/api/list_indexes.html | 84 +++
docs/version/8.0.0/api/overview.html | 59 ++
docs/version/8.0.0/api/plugins.html | 121 ++++
docs/version/8.0.0/api/purge.html | 79 +++
docs/version/8.0.0/api/query_database.html | 459 +++++++++++++++
docs/version/8.0.0/api/query_index.html | 338 +++++++++++
docs/version/8.0.0/api/replication.html | 322 +++++++++++
docs/version/8.0.0/api/revisions_diff.html | 65 +++
docs/version/8.0.0/api/save_attachment.html | 349 ++++++++++++
docs/version/8.0.0/api/sync.html | 96 ++++
docs/version/8.0.0/api/view_cleanup.html | 47 ++
docs/version/8.0.0/custom.md | 539 ++++++++++++++++++
docs/version/8.0.0/download.md | 75 +++
docs/version/8.0.0/errors.md | 321 +++++++++++
docs/version/8.0.0/external.md | 306 ++++++++++
docs/version/8.0.0/faq.md | 147 +++++
docs/version/8.0.0/getting-started.md | 215 +++++++
docs/version/8.0.0/guides/async-code.md | 211 +++++++
docs/version/8.0.0/guides/attachments.md | 314 ++++++++++
docs/version/8.0.0/guides/bulk-operations.md | 132 +++++
docs/version/8.0.0/guides/changes.md | 142 +++++
.../8.0.0/guides/compact-and-destroy.md | 97 ++++
docs/version/8.0.0/guides/conflicts.md | 160 ++++++
docs/version/8.0.0/guides/databases.md | 146 +++++
docs/version/8.0.0/guides/documents.md | 191 +++++++
docs/version/8.0.0/guides/guides.json | 3 +
docs/version/8.0.0/guides/index.md | 56 ++
docs/version/8.0.0/guides/local-documents.md | 44 ++
docs/version/8.0.0/guides/mango-queries.md | 364 ++++++++++++
docs/version/8.0.0/guides/queries.md | 240 ++++++++
docs/version/8.0.0/guides/replication.md | 194 +++++++
docs/version/8.0.0/guides/setup-couchdb.md | 103 ++++
docs/version/8.0.0/guides/setup-pouchdb.md | 97 ++++
.../version/8.0.0/guides/updating-deleting.md | 145 +++++
docs/version/8.0.0/learn.md | 38 ++
docs/version/8.0.0/offline.md | 11 +
docs/version/8.0.0/users.md | 132 +++++
docs/version/9.0.0/adapters.md | 220 +++++++
docs/version/9.0.0/api.html | 42 ++
docs/version/9.0.0/api/active_tasks.html | 45 ++
docs/version/9.0.0/api/batch_create.html | 271 +++++++++
docs/version/9.0.0/api/batch_fetch.html | 205 +++++++
docs/version/9.0.0/api/bulk_get.html | 118 ++++
docs/version/9.0.0/api/changes.html | 358 ++++++++++++
docs/version/9.0.0/api/close_database.html | 33 ++
docs/version/9.0.0/api/compaction.html | 49 ++
docs/version/9.0.0/api/create_database.html | 64 +++
docs/version/9.0.0/api/create_document.html | 174 ++++++
docs/version/9.0.0/api/create_index.html | 302 ++++++++++
.../9.0.0/api/database_information.html | 59 ++
docs/version/9.0.0/api/defaults.html | 42 ++
docs/version/9.0.0/api/delete_attachment.html | 52 ++
docs/version/9.0.0/api/delete_database.html | 50 ++
docs/version/9.0.0/api/delete_document.html | 140 +++++
docs/version/9.0.0/api/delete_index.html | 129 +++++
docs/version/9.0.0/api/events.html | 14 +
docs/version/9.0.0/api/explain_index.html | 135 +++++
docs/version/9.0.0/api/fetch_document.html | 64 +++
docs/version/9.0.0/api/get_attachment.html | 116 ++++
docs/version/9.0.0/api/list_indexes.html | 84 +++
docs/version/9.0.0/api/overview.html | 59 ++
docs/version/9.0.0/api/plugins.html | 121 ++++
docs/version/9.0.0/api/purge.html | 79 +++
docs/version/9.0.0/api/query_database.html | 459 +++++++++++++++
docs/version/9.0.0/api/query_index.html | 338 +++++++++++
docs/version/9.0.0/api/replication.html | 322 +++++++++++
docs/version/9.0.0/api/revisions_diff.html | 65 +++
docs/version/9.0.0/api/save_attachment.html | 349 ++++++++++++
docs/version/9.0.0/api/sync.html | 96 ++++
docs/version/9.0.0/api/view_cleanup.html | 47 ++
docs/version/9.0.0/custom.md | 539 ++++++++++++++++++
docs/version/9.0.0/download.md | 75 +++
docs/version/9.0.0/errors.md | 321 +++++++++++
docs/version/9.0.0/external.md | 306 ++++++++++
docs/version/9.0.0/faq.md | 147 +++++
docs/version/9.0.0/getting-started.md | 215 +++++++
docs/version/9.0.0/guides/async-code.md | 211 +++++++
docs/version/9.0.0/guides/attachments.md | 314 ++++++++++
docs/version/9.0.0/guides/bulk-operations.md | 132 +++++
docs/version/9.0.0/guides/changes.md | 142 +++++
.../9.0.0/guides/compact-and-destroy.md | 97 ++++
docs/version/9.0.0/guides/conflicts.md | 160 ++++++
docs/version/9.0.0/guides/databases.md | 146 +++++
docs/version/9.0.0/guides/documents.md | 191 +++++++
docs/version/9.0.0/guides/guides.json | 3 +
docs/version/9.0.0/guides/index.md | 56 ++
docs/version/9.0.0/guides/local-documents.md | 44 ++
docs/version/9.0.0/guides/mango-queries.md | 364 ++++++++++++
docs/version/9.0.0/guides/queries.md | 240 ++++++++
docs/version/9.0.0/guides/replication.md | 194 +++++++
docs/version/9.0.0/guides/setup-couchdb.md | 103 ++++
docs/version/9.0.0/guides/setup-pouchdb.md | 97 ++++
.../version/9.0.0/guides/updating-deleting.md | 145 +++++
docs/version/9.0.0/learn.md | 38 ++
docs/version/9.0.0/offline.md | 11 +
docs/version/9.0.0/users.md | 132 +++++
docs/version/latest/adapters.md | 220 +++++++
docs/version/latest/api.html | 42 ++
docs/version/latest/api/active_tasks.html | 45 ++
docs/version/latest/api/batch_create.html | 271 +++++++++
docs/version/latest/api/batch_fetch.html | 205 +++++++
docs/version/latest/api/bulk_get.html | 118 ++++
docs/version/latest/api/changes.html | 358 ++++++++++++
docs/version/latest/api/close_database.html | 33 ++
docs/version/latest/api/compaction.html | 49 ++
docs/version/latest/api/create_database.html | 64 +++
docs/version/latest/api/create_document.html | 174 ++++++
docs/version/latest/api/create_index.html | 302 ++++++++++
.../latest/api/database_information.html | 59 ++
docs/version/latest/api/defaults.html | 42 ++
.../version/latest/api/delete_attachment.html | 52 ++
docs/version/latest/api/delete_database.html | 50 ++
docs/version/latest/api/delete_document.html | 140 +++++
docs/version/latest/api/delete_index.html | 129 +++++
docs/version/latest/api/events.html | 14 +
docs/version/latest/api/explain_index.html | 135 +++++
docs/version/latest/api/fetch_document.html | 64 +++
docs/version/latest/api/get_attachment.html | 116 ++++
docs/version/latest/api/list_indexes.html | 84 +++
docs/version/latest/api/overview.html | 59 ++
docs/version/latest/api/plugins.html | 121 ++++
docs/version/latest/api/purge.html | 79 +++
docs/version/latest/api/query_database.html | 459 +++++++++++++++
docs/version/latest/api/query_index.html | 338 +++++++++++
docs/version/latest/api/replication.html | 322 +++++++++++
docs/version/latest/api/revisions_diff.html | 65 +++
docs/version/latest/api/save_attachment.html | 349 ++++++++++++
docs/version/latest/api/sync.html | 96 ++++
docs/version/latest/api/view_cleanup.html | 47 ++
docs/version/latest/custom.md | 539 ++++++++++++++++++
docs/version/latest/download.md | 75 +++
docs/version/latest/errors.md | 321 +++++++++++
docs/version/latest/external.md | 306 ++++++++++
docs/version/latest/faq.md | 147 +++++
docs/version/latest/getting-started.md | 215 +++++++
docs/version/latest/guides/async-code.md | 211 +++++++
docs/version/latest/guides/attachments.md | 314 ++++++++++
docs/version/latest/guides/bulk-operations.md | 132 +++++
docs/version/latest/guides/changes.md | 142 +++++
.../latest/guides/compact-and-destroy.md | 97 ++++
docs/version/latest/guides/conflicts.md | 160 ++++++
docs/version/latest/guides/databases.md | 146 +++++
docs/version/latest/guides/documents.md | 191 +++++++
docs/version/latest/guides/guides.json | 3 +
docs/version/latest/guides/index.md | 56 ++
docs/version/latest/guides/local-documents.md | 44 ++
docs/version/latest/guides/mango-queries.md | 364 ++++++++++++
docs/version/latest/guides/queries.md | 240 ++++++++
docs/version/latest/guides/replication.md | 194 +++++++
docs/version/latest/guides/setup-couchdb.md | 103 ++++
docs/version/latest/guides/setup-pouchdb.md | 97 ++++
.../latest/guides/updating-deleting.md | 145 +++++
docs/version/latest/learn.md | 38 ++
docs/version/latest/offline.md | 11 +
docs/version/latest/users.md | 132 +++++
238 files changed, 27424 insertions(+), 52 deletions(-)
delete mode 100644 docs/api.html
rename docs/{ => version/7.3.0}/adapters.md (99%)
create mode 100644 docs/version/7.3.0/api.html
rename docs/{_includes => version/7.3.0}/api/active_tasks.html (100%)
rename docs/{_includes => version/7.3.0}/api/batch_create.html (100%)
rename docs/{_includes => version/7.3.0}/api/batch_fetch.html (100%)
rename docs/{_includes => version/7.3.0}/api/bulk_get.html (100%)
rename docs/{_includes => version/7.3.0}/api/changes.html (100%)
rename docs/{_includes => version/7.3.0}/api/close_database.html (100%)
rename docs/{_includes => version/7.3.0}/api/compaction.html (100%)
rename docs/{_includes => version/7.3.0}/api/create_database.html (100%)
rename docs/{_includes => version/7.3.0}/api/create_document.html (100%)
rename docs/{_includes => version/7.3.0}/api/create_index.html (100%)
rename docs/{_includes => version/7.3.0}/api/database_information.html (100%)
rename docs/{_includes => version/7.3.0}/api/defaults.html (100%)
rename docs/{_includes => version/7.3.0}/api/delete_attachment.html (100%)
rename docs/{_includes => version/7.3.0}/api/delete_database.html (100%)
rename docs/{_includes => version/7.3.0}/api/delete_document.html (100%)
rename docs/{_includes => version/7.3.0}/api/delete_index.html (100%)
rename docs/{_includes => version/7.3.0}/api/events.html (100%)
rename docs/{_includes => version/7.3.0}/api/explain_index.html (100%)
rename docs/{_includes => version/7.3.0}/api/fetch_document.html (100%)
rename docs/{_includes => version/7.3.0}/api/get_attachment.html (100%)
rename docs/{_includes => version/7.3.0}/api/list_indexes.html (100%)
rename docs/{_includes => version/7.3.0}/api/overview.html (100%)
rename docs/{_includes => version/7.3.0}/api/plugins.html (100%)
rename docs/{_includes => version/7.3.0}/api/purge.html (100%)
rename docs/{_includes => version/7.3.0}/api/query_database.html (100%)
rename docs/{_includes => version/7.3.0}/api/query_index.html (100%)
rename docs/{_includes => version/7.3.0}/api/replication.html (100%)
rename docs/{_includes => version/7.3.0}/api/revisions_diff.html (100%)
rename docs/{_includes => version/7.3.0}/api/save_attachment.html (100%)
rename docs/{_includes => version/7.3.0}/api/sync.html (100%)
rename docs/{_includes => version/7.3.0}/api/view_cleanup.html (100%)
rename docs/{ => version/7.3.0}/custom.md (99%)
rename docs/{ => version/7.3.0}/download.md (99%)
rename docs/{ => version/7.3.0}/errors.md (99%)
rename docs/{ => version/7.3.0}/external.md (99%)
rename docs/{ => version/7.3.0}/faq.md (99%)
rename docs/{ => version/7.3.0}/getting-started.md (99%)
rename docs/{ => version/7.3.0}/guides/async-code.md (100%)
rename docs/{ => version/7.3.0}/guides/attachments.md (100%)
rename docs/{ => version/7.3.0}/guides/bulk-operations.md (100%)
rename docs/{ => version/7.3.0}/guides/changes.md (100%)
rename docs/{ => version/7.3.0}/guides/compact-and-destroy.md (100%)
rename docs/{ => version/7.3.0}/guides/conflicts.md (100%)
rename docs/{ => version/7.3.0}/guides/databases.md (100%)
rename docs/{ => version/7.3.0}/guides/documents.md (100%)
rename docs/{ => version/7.3.0}/guides/guides.json (100%)
rename docs/{ => version/7.3.0}/guides/index.md (100%)
rename docs/{ => version/7.3.0}/guides/local-documents.md (100%)
rename docs/{ => version/7.3.0}/guides/mango-queries.md (100%)
rename docs/{ => version/7.3.0}/guides/queries.md (100%)
rename docs/{ => version/7.3.0}/guides/replication.md (100%)
rename docs/{ => version/7.3.0}/guides/setup-couchdb.md (100%)
rename docs/{ => version/7.3.0}/guides/setup-pouchdb.md (100%)
rename docs/{ => version/7.3.0}/guides/updating-deleting.md (100%)
rename docs/{ => version/7.3.0}/learn.md (99%)
rename docs/{ => version/7.3.0}/offline.md (100%)
rename docs/{ => version/7.3.0}/users.md (99%)
create mode 100644 docs/version/8.0.0/adapters.md
create mode 100644 docs/version/8.0.0/api.html
create mode 100644 docs/version/8.0.0/api/active_tasks.html
create mode 100644 docs/version/8.0.0/api/batch_create.html
create mode 100644 docs/version/8.0.0/api/batch_fetch.html
create mode 100644 docs/version/8.0.0/api/bulk_get.html
create mode 100644 docs/version/8.0.0/api/changes.html
create mode 100644 docs/version/8.0.0/api/close_database.html
create mode 100644 docs/version/8.0.0/api/compaction.html
create mode 100644 docs/version/8.0.0/api/create_database.html
create mode 100644 docs/version/8.0.0/api/create_document.html
create mode 100644 docs/version/8.0.0/api/create_index.html
create mode 100644 docs/version/8.0.0/api/database_information.html
create mode 100644 docs/version/8.0.0/api/defaults.html
create mode 100644 docs/version/8.0.0/api/delete_attachment.html
create mode 100644 docs/version/8.0.0/api/delete_database.html
create mode 100644 docs/version/8.0.0/api/delete_document.html
create mode 100644 docs/version/8.0.0/api/delete_index.html
create mode 100644 docs/version/8.0.0/api/events.html
create mode 100644 docs/version/8.0.0/api/explain_index.html
create mode 100644 docs/version/8.0.0/api/fetch_document.html
create mode 100644 docs/version/8.0.0/api/get_attachment.html
create mode 100644 docs/version/8.0.0/api/list_indexes.html
create mode 100644 docs/version/8.0.0/api/overview.html
create mode 100644 docs/version/8.0.0/api/plugins.html
create mode 100644 docs/version/8.0.0/api/purge.html
create mode 100644 docs/version/8.0.0/api/query_database.html
create mode 100644 docs/version/8.0.0/api/query_index.html
create mode 100644 docs/version/8.0.0/api/replication.html
create mode 100644 docs/version/8.0.0/api/revisions_diff.html
create mode 100644 docs/version/8.0.0/api/save_attachment.html
create mode 100644 docs/version/8.0.0/api/sync.html
create mode 100644 docs/version/8.0.0/api/view_cleanup.html
create mode 100644 docs/version/8.0.0/custom.md
create mode 100644 docs/version/8.0.0/download.md
create mode 100644 docs/version/8.0.0/errors.md
create mode 100644 docs/version/8.0.0/external.md
create mode 100644 docs/version/8.0.0/faq.md
create mode 100644 docs/version/8.0.0/getting-started.md
create mode 100644 docs/version/8.0.0/guides/async-code.md
create mode 100644 docs/version/8.0.0/guides/attachments.md
create mode 100644 docs/version/8.0.0/guides/bulk-operations.md
create mode 100644 docs/version/8.0.0/guides/changes.md
create mode 100644 docs/version/8.0.0/guides/compact-and-destroy.md
create mode 100644 docs/version/8.0.0/guides/conflicts.md
create mode 100644 docs/version/8.0.0/guides/databases.md
create mode 100644 docs/version/8.0.0/guides/documents.md
create mode 100644 docs/version/8.0.0/guides/guides.json
create mode 100644 docs/version/8.0.0/guides/index.md
create mode 100644 docs/version/8.0.0/guides/local-documents.md
create mode 100644 docs/version/8.0.0/guides/mango-queries.md
create mode 100644 docs/version/8.0.0/guides/queries.md
create mode 100644 docs/version/8.0.0/guides/replication.md
create mode 100644 docs/version/8.0.0/guides/setup-couchdb.md
create mode 100644 docs/version/8.0.0/guides/setup-pouchdb.md
create mode 100644 docs/version/8.0.0/guides/updating-deleting.md
create mode 100644 docs/version/8.0.0/learn.md
create mode 100644 docs/version/8.0.0/offline.md
create mode 100644 docs/version/8.0.0/users.md
create mode 100644 docs/version/9.0.0/adapters.md
create mode 100644 docs/version/9.0.0/api.html
create mode 100644 docs/version/9.0.0/api/active_tasks.html
create mode 100644 docs/version/9.0.0/api/batch_create.html
create mode 100644 docs/version/9.0.0/api/batch_fetch.html
create mode 100644 docs/version/9.0.0/api/bulk_get.html
create mode 100644 docs/version/9.0.0/api/changes.html
create mode 100644 docs/version/9.0.0/api/close_database.html
create mode 100644 docs/version/9.0.0/api/compaction.html
create mode 100644 docs/version/9.0.0/api/create_database.html
create mode 100644 docs/version/9.0.0/api/create_document.html
create mode 100644 docs/version/9.0.0/api/create_index.html
create mode 100644 docs/version/9.0.0/api/database_information.html
create mode 100644 docs/version/9.0.0/api/defaults.html
create mode 100644 docs/version/9.0.0/api/delete_attachment.html
create mode 100644 docs/version/9.0.0/api/delete_database.html
create mode 100644 docs/version/9.0.0/api/delete_document.html
create mode 100644 docs/version/9.0.0/api/delete_index.html
create mode 100644 docs/version/9.0.0/api/events.html
create mode 100644 docs/version/9.0.0/api/explain_index.html
create mode 100644 docs/version/9.0.0/api/fetch_document.html
create mode 100644 docs/version/9.0.0/api/get_attachment.html
create mode 100644 docs/version/9.0.0/api/list_indexes.html
create mode 100644 docs/version/9.0.0/api/overview.html
create mode 100644 docs/version/9.0.0/api/plugins.html
create mode 100644 docs/version/9.0.0/api/purge.html
create mode 100644 docs/version/9.0.0/api/query_database.html
create mode 100644 docs/version/9.0.0/api/query_index.html
create mode 100644 docs/version/9.0.0/api/replication.html
create mode 100644 docs/version/9.0.0/api/revisions_diff.html
create mode 100644 docs/version/9.0.0/api/save_attachment.html
create mode 100644 docs/version/9.0.0/api/sync.html
create mode 100644 docs/version/9.0.0/api/view_cleanup.html
create mode 100644 docs/version/9.0.0/custom.md
create mode 100644 docs/version/9.0.0/download.md
create mode 100644 docs/version/9.0.0/errors.md
create mode 100644 docs/version/9.0.0/external.md
create mode 100644 docs/version/9.0.0/faq.md
create mode 100644 docs/version/9.0.0/getting-started.md
create mode 100644 docs/version/9.0.0/guides/async-code.md
create mode 100644 docs/version/9.0.0/guides/attachments.md
create mode 100644 docs/version/9.0.0/guides/bulk-operations.md
create mode 100644 docs/version/9.0.0/guides/changes.md
create mode 100644 docs/version/9.0.0/guides/compact-and-destroy.md
create mode 100644 docs/version/9.0.0/guides/conflicts.md
create mode 100644 docs/version/9.0.0/guides/databases.md
create mode 100644 docs/version/9.0.0/guides/documents.md
create mode 100644 docs/version/9.0.0/guides/guides.json
create mode 100644 docs/version/9.0.0/guides/index.md
create mode 100644 docs/version/9.0.0/guides/local-documents.md
create mode 100644 docs/version/9.0.0/guides/mango-queries.md
create mode 100644 docs/version/9.0.0/guides/queries.md
create mode 100644 docs/version/9.0.0/guides/replication.md
create mode 100644 docs/version/9.0.0/guides/setup-couchdb.md
create mode 100644 docs/version/9.0.0/guides/setup-pouchdb.md
create mode 100644 docs/version/9.0.0/guides/updating-deleting.md
create mode 100644 docs/version/9.0.0/learn.md
create mode 100644 docs/version/9.0.0/offline.md
create mode 100644 docs/version/9.0.0/users.md
create mode 100644 docs/version/latest/adapters.md
create mode 100644 docs/version/latest/api.html
create mode 100644 docs/version/latest/api/active_tasks.html
create mode 100644 docs/version/latest/api/batch_create.html
create mode 100644 docs/version/latest/api/batch_fetch.html
create mode 100644 docs/version/latest/api/bulk_get.html
create mode 100644 docs/version/latest/api/changes.html
create mode 100644 docs/version/latest/api/close_database.html
create mode 100644 docs/version/latest/api/compaction.html
create mode 100644 docs/version/latest/api/create_database.html
create mode 100644 docs/version/latest/api/create_document.html
create mode 100644 docs/version/latest/api/create_index.html
create mode 100644 docs/version/latest/api/database_information.html
create mode 100644 docs/version/latest/api/defaults.html
create mode 100644 docs/version/latest/api/delete_attachment.html
create mode 100644 docs/version/latest/api/delete_database.html
create mode 100644 docs/version/latest/api/delete_document.html
create mode 100644 docs/version/latest/api/delete_index.html
create mode 100644 docs/version/latest/api/events.html
create mode 100644 docs/version/latest/api/explain_index.html
create mode 100644 docs/version/latest/api/fetch_document.html
create mode 100644 docs/version/latest/api/get_attachment.html
create mode 100644 docs/version/latest/api/list_indexes.html
create mode 100644 docs/version/latest/api/overview.html
create mode 100644 docs/version/latest/api/plugins.html
create mode 100644 docs/version/latest/api/purge.html
create mode 100644 docs/version/latest/api/query_database.html
create mode 100644 docs/version/latest/api/query_index.html
create mode 100644 docs/version/latest/api/replication.html
create mode 100644 docs/version/latest/api/revisions_diff.html
create mode 100644 docs/version/latest/api/save_attachment.html
create mode 100644 docs/version/latest/api/sync.html
create mode 100644 docs/version/latest/api/view_cleanup.html
create mode 100644 docs/version/latest/custom.md
create mode 100644 docs/version/latest/download.md
create mode 100644 docs/version/latest/errors.md
create mode 100644 docs/version/latest/external.md
create mode 100644 docs/version/latest/faq.md
create mode 100644 docs/version/latest/getting-started.md
create mode 100644 docs/version/latest/guides/async-code.md
create mode 100644 docs/version/latest/guides/attachments.md
create mode 100644 docs/version/latest/guides/bulk-operations.md
create mode 100644 docs/version/latest/guides/changes.md
create mode 100644 docs/version/latest/guides/compact-and-destroy.md
create mode 100644 docs/version/latest/guides/conflicts.md
create mode 100644 docs/version/latest/guides/databases.md
create mode 100644 docs/version/latest/guides/documents.md
create mode 100644 docs/version/latest/guides/guides.json
create mode 100644 docs/version/latest/guides/index.md
create mode 100644 docs/version/latest/guides/local-documents.md
create mode 100644 docs/version/latest/guides/mango-queries.md
create mode 100644 docs/version/latest/guides/queries.md
create mode 100644 docs/version/latest/guides/replication.md
create mode 100644 docs/version/latest/guides/setup-couchdb.md
create mode 100644 docs/version/latest/guides/setup-pouchdb.md
create mode 100644 docs/version/latest/guides/updating-deleting.md
create mode 100644 docs/version/latest/learn.md
create mode 100644 docs/version/latest/offline.md
create mode 100644 docs/version/latest/users.md
diff --git a/docs/_data/versions.js b/docs/_data/versions.js
index 44bb81bfa1..e1379a1d6f 100644
--- a/docs/_data/versions.js
+++ b/docs/_data/versions.js
@@ -8,7 +8,7 @@ Gives us globally available info about PouchDB versions:
*/
module.exports = () => {
- const versions = ["7.0.0", "8.0.0", "9.0.0"];
+ const versions = ["7.3.0", "8.0.0", "9.0.0"];
return {
all: versions,
stable: versions.sort((a, b) =>
diff --git a/docs/api.html b/docs/api.html
deleted file mode 100644
index d4dab2614f..0000000000
--- a/docs/api.html
+++ /dev/null
@@ -1,42 +0,0 @@
----
-layout: 2ColLeft.html
-title: API Reference
-sidebar: api.html
-edit: false
----
-
-{% markdown %}
-
-{% include api/overview.html %}
-{% include api/create_database.html %}
-{% include api/delete_database.html %}
-{% include api/create_document.html %}
-{% include api/fetch_document.html %}
-{% include api/delete_document.html %}
-{% include api/batch_create.html %}
-{% include api/batch_fetch.html %}
-{% include api/changes.html %}
-{% include api/replication.html %}
-{% include api/sync.html %}
-{% include api/save_attachment.html %}
-{% include api/get_attachment.html %}
-{% include api/delete_attachment.html %}
-{% include api/create_index.html %}
-{% include api/query_index.html %}
-{% include api/explain_index.html %}
-{% include api/list_indexes.html %}
-{% include api/delete_index.html %}
-{% include api/query_database.html %}
-{% include api/view_cleanup.html %}
-{% include api/database_information.html %}
-{% include api/compaction.html %}
-{% include api/revisions_diff.html %}
-{% include api/bulk_get.html %}
-{% include api/close_database.html %}
-{% include api/purge.html %}
-{% include api/events.html %}
-{% include api/active_tasks.html %}
-{% include api/defaults.html %}
-{% include api/plugins.html %}
-
-{% endmarkdown %}
diff --git a/docs/adapters.md b/docs/version/7.3.0/adapters.md
similarity index 99%
rename from docs/adapters.md
rename to docs/version/7.3.0/adapters.md
index 218c0b8566..cf09c5dd44 100644
--- a/docs/adapters.md
+++ b/docs/version/7.3.0/adapters.md
@@ -1,7 +1,7 @@
---
layout: 2ColLeft.html
title: Adapters
-sidebar: nav.html
+sidebar: ./nav.html
---
PouchDB is not a self-contained database; it is a CouchDB-style abstraction layer over other databases. By default, PouchDB ships with the [IndexedDB][] adapter for the browser, and a [LevelDB][] adapter in Node.js. This can be visualized as so:
diff --git a/docs/version/7.3.0/api.html b/docs/version/7.3.0/api.html
new file mode 100644
index 0000000000..0befc9164c
--- /dev/null
+++ b/docs/version/7.3.0/api.html
@@ -0,0 +1,42 @@
+---
+layout: 2ColLeft.html
+title: "API Reference"
+sidebar: api.html
+edit: false
+---
+
+{% markdown %}
+
+{% include ./api/overview.html %}
+{% include ./api/create_database.html %}
+{% include ./api/delete_database.html %}
+{% include ./api/create_document.html %}
+{% include ./api/fetch_document.html %}
+{% include ./api/delete_document.html %}
+{% include ./api/batch_create.html %}
+{% include ./api/batch_fetch.html %}
+{% include ./api/changes.html %}
+{% include ./api/replication.html %}
+{% include ./api/sync.html %}
+{% include ./api/save_attachment.html %}
+{% include ./api/get_attachment.html %}
+{% include ./api/delete_attachment.html %}
+{% include ./api/create_index.html %}
+{% include ./api/query_index.html %}
+{% include ./api/explain_index.html %}
+{% include ./api/list_indexes.html %}
+{% include ./api/delete_index.html %}
+{% include ./api/query_database.html %}
+{% include ./api/view_cleanup.html %}
+{% include ./api/database_information.html %}
+{% include ./api/compaction.html %}
+{% include ./api/revisions_diff.html %}
+{% include ./api/bulk_get.html %}
+{% include ./api/close_database.html %}
+{% include ./api/purge.html %}
+{% include ./api/events.html %}
+{% include ./api/active_tasks.html %}
+{% include ./api/defaults.html %}
+{% include ./api/plugins.html %}
+
+{% endmarkdown %}
diff --git a/docs/_includes/api/active_tasks.html b/docs/version/7.3.0/api/active_tasks.html
similarity index 100%
rename from docs/_includes/api/active_tasks.html
rename to docs/version/7.3.0/api/active_tasks.html
diff --git a/docs/_includes/api/batch_create.html b/docs/version/7.3.0/api/batch_create.html
similarity index 100%
rename from docs/_includes/api/batch_create.html
rename to docs/version/7.3.0/api/batch_create.html
diff --git a/docs/_includes/api/batch_fetch.html b/docs/version/7.3.0/api/batch_fetch.html
similarity index 100%
rename from docs/_includes/api/batch_fetch.html
rename to docs/version/7.3.0/api/batch_fetch.html
diff --git a/docs/_includes/api/bulk_get.html b/docs/version/7.3.0/api/bulk_get.html
similarity index 100%
rename from docs/_includes/api/bulk_get.html
rename to docs/version/7.3.0/api/bulk_get.html
diff --git a/docs/_includes/api/changes.html b/docs/version/7.3.0/api/changes.html
similarity index 100%
rename from docs/_includes/api/changes.html
rename to docs/version/7.3.0/api/changes.html
diff --git a/docs/_includes/api/close_database.html b/docs/version/7.3.0/api/close_database.html
similarity index 100%
rename from docs/_includes/api/close_database.html
rename to docs/version/7.3.0/api/close_database.html
diff --git a/docs/_includes/api/compaction.html b/docs/version/7.3.0/api/compaction.html
similarity index 100%
rename from docs/_includes/api/compaction.html
rename to docs/version/7.3.0/api/compaction.html
diff --git a/docs/_includes/api/create_database.html b/docs/version/7.3.0/api/create_database.html
similarity index 100%
rename from docs/_includes/api/create_database.html
rename to docs/version/7.3.0/api/create_database.html
diff --git a/docs/_includes/api/create_document.html b/docs/version/7.3.0/api/create_document.html
similarity index 100%
rename from docs/_includes/api/create_document.html
rename to docs/version/7.3.0/api/create_document.html
diff --git a/docs/_includes/api/create_index.html b/docs/version/7.3.0/api/create_index.html
similarity index 100%
rename from docs/_includes/api/create_index.html
rename to docs/version/7.3.0/api/create_index.html
diff --git a/docs/_includes/api/database_information.html b/docs/version/7.3.0/api/database_information.html
similarity index 100%
rename from docs/_includes/api/database_information.html
rename to docs/version/7.3.0/api/database_information.html
diff --git a/docs/_includes/api/defaults.html b/docs/version/7.3.0/api/defaults.html
similarity index 100%
rename from docs/_includes/api/defaults.html
rename to docs/version/7.3.0/api/defaults.html
diff --git a/docs/_includes/api/delete_attachment.html b/docs/version/7.3.0/api/delete_attachment.html
similarity index 100%
rename from docs/_includes/api/delete_attachment.html
rename to docs/version/7.3.0/api/delete_attachment.html
diff --git a/docs/_includes/api/delete_database.html b/docs/version/7.3.0/api/delete_database.html
similarity index 100%
rename from docs/_includes/api/delete_database.html
rename to docs/version/7.3.0/api/delete_database.html
diff --git a/docs/_includes/api/delete_document.html b/docs/version/7.3.0/api/delete_document.html
similarity index 100%
rename from docs/_includes/api/delete_document.html
rename to docs/version/7.3.0/api/delete_document.html
diff --git a/docs/_includes/api/delete_index.html b/docs/version/7.3.0/api/delete_index.html
similarity index 100%
rename from docs/_includes/api/delete_index.html
rename to docs/version/7.3.0/api/delete_index.html
diff --git a/docs/_includes/api/events.html b/docs/version/7.3.0/api/events.html
similarity index 100%
rename from docs/_includes/api/events.html
rename to docs/version/7.3.0/api/events.html
diff --git a/docs/_includes/api/explain_index.html b/docs/version/7.3.0/api/explain_index.html
similarity index 100%
rename from docs/_includes/api/explain_index.html
rename to docs/version/7.3.0/api/explain_index.html
diff --git a/docs/_includes/api/fetch_document.html b/docs/version/7.3.0/api/fetch_document.html
similarity index 100%
rename from docs/_includes/api/fetch_document.html
rename to docs/version/7.3.0/api/fetch_document.html
diff --git a/docs/_includes/api/get_attachment.html b/docs/version/7.3.0/api/get_attachment.html
similarity index 100%
rename from docs/_includes/api/get_attachment.html
rename to docs/version/7.3.0/api/get_attachment.html
diff --git a/docs/_includes/api/list_indexes.html b/docs/version/7.3.0/api/list_indexes.html
similarity index 100%
rename from docs/_includes/api/list_indexes.html
rename to docs/version/7.3.0/api/list_indexes.html
diff --git a/docs/_includes/api/overview.html b/docs/version/7.3.0/api/overview.html
similarity index 100%
rename from docs/_includes/api/overview.html
rename to docs/version/7.3.0/api/overview.html
diff --git a/docs/_includes/api/plugins.html b/docs/version/7.3.0/api/plugins.html
similarity index 100%
rename from docs/_includes/api/plugins.html
rename to docs/version/7.3.0/api/plugins.html
diff --git a/docs/_includes/api/purge.html b/docs/version/7.3.0/api/purge.html
similarity index 100%
rename from docs/_includes/api/purge.html
rename to docs/version/7.3.0/api/purge.html
diff --git a/docs/_includes/api/query_database.html b/docs/version/7.3.0/api/query_database.html
similarity index 100%
rename from docs/_includes/api/query_database.html
rename to docs/version/7.3.0/api/query_database.html
diff --git a/docs/_includes/api/query_index.html b/docs/version/7.3.0/api/query_index.html
similarity index 100%
rename from docs/_includes/api/query_index.html
rename to docs/version/7.3.0/api/query_index.html
diff --git a/docs/_includes/api/replication.html b/docs/version/7.3.0/api/replication.html
similarity index 100%
rename from docs/_includes/api/replication.html
rename to docs/version/7.3.0/api/replication.html
diff --git a/docs/_includes/api/revisions_diff.html b/docs/version/7.3.0/api/revisions_diff.html
similarity index 100%
rename from docs/_includes/api/revisions_diff.html
rename to docs/version/7.3.0/api/revisions_diff.html
diff --git a/docs/_includes/api/save_attachment.html b/docs/version/7.3.0/api/save_attachment.html
similarity index 100%
rename from docs/_includes/api/save_attachment.html
rename to docs/version/7.3.0/api/save_attachment.html
diff --git a/docs/_includes/api/sync.html b/docs/version/7.3.0/api/sync.html
similarity index 100%
rename from docs/_includes/api/sync.html
rename to docs/version/7.3.0/api/sync.html
diff --git a/docs/_includes/api/view_cleanup.html b/docs/version/7.3.0/api/view_cleanup.html
similarity index 100%
rename from docs/_includes/api/view_cleanup.html
rename to docs/version/7.3.0/api/view_cleanup.html
diff --git a/docs/custom.md b/docs/version/7.3.0/custom.md
similarity index 99%
rename from docs/custom.md
rename to docs/version/7.3.0/custom.md
index 229c2cae4f..b8095cc4aa 100644
--- a/docs/custom.md
+++ b/docs/version/7.3.0/custom.md
@@ -1,7 +1,7 @@
---
layout: 2ColLeft.html
title: Custom Builds
-sidebar: nav.html
+sidebar: ./nav.html
---
PouchDB supports custom builds, meaning you can pick and choose the features of
diff --git a/docs/download.md b/docs/version/7.3.0/download.md
similarity index 99%
rename from docs/download.md
rename to docs/version/7.3.0/download.md
index 769689ce82..d0578c6d29 100644
--- a/docs/download.md
+++ b/docs/version/7.3.0/download.md
@@ -1,7 +1,7 @@
---
layout: 2ColLeft.html
title: Download
-sidebar: nav.html
+sidebar: ./nav.html
---
Apache PouchDB™ is an open-source JavaScript database inspired by Apache CouchDB that is designed to run well within the browser.
diff --git a/docs/errors.md b/docs/version/7.3.0/errors.md
similarity index 99%
rename from docs/errors.md
rename to docs/version/7.3.0/errors.md
index 48323e4bcf..9fa2b7ba8e 100644
--- a/docs/errors.md
+++ b/docs/version/7.3.0/errors.md
@@ -1,7 +1,7 @@
---
layout: 2ColLeft.html
title: Common Errors
-sidebar: nav.html
+sidebar: ./nav.html
---
{% include anchor.html class="h3" title="No `Access-Control-Allow-Origin` header" hash="no_access_control_allow_origin_header" %}
diff --git a/docs/external.md b/docs/version/7.3.0/external.md
similarity index 99%
rename from docs/external.md
rename to docs/version/7.3.0/external.md
index a80d144a84..95cbc41816 100644
--- a/docs/external.md
+++ b/docs/version/7.3.0/external.md
@@ -1,7 +1,7 @@
---
layout: 2ColLeft.html
title: Plugins and External Projects
-sidebar: nav.html
+sidebar: ./nav.html
---
Below is a list of known plugins, tools and projects can be used with PouchDB.
diff --git a/docs/faq.md b/docs/version/7.3.0/faq.md
similarity index 99%
rename from docs/faq.md
rename to docs/version/7.3.0/faq.md
index bd831ff784..411ed8fc09 100644
--- a/docs/faq.md
+++ b/docs/version/7.3.0/faq.md
@@ -1,7 +1,7 @@
---
layout: 2ColLeft.html
title: FAQ
-sidebar: nav.html
+sidebar: ./nav.html
---
{% include anchor.html class="h3" title="Can PouchDB sync with MongoDB/MySQL/my current non-CouchDB database?" hash="sync_non_couchdb" %}
diff --git a/docs/getting-started.md b/docs/version/7.3.0/getting-started.md
similarity index 99%
rename from docs/getting-started.md
rename to docs/version/7.3.0/getting-started.md
index c471acd48f..97279883f2 100644
--- a/docs/getting-started.md
+++ b/docs/version/7.3.0/getting-started.md
@@ -1,7 +1,7 @@
---
layout: 2ColLeft.html
title: Getting Started Guide
-sidebar: nav.html
+sidebar: ./nav.html
---
{% include alert/start.html variant="info" %}
diff --git a/docs/guides/async-code.md b/docs/version/7.3.0/guides/async-code.md
similarity index 100%
rename from docs/guides/async-code.md
rename to docs/version/7.3.0/guides/async-code.md
diff --git a/docs/guides/attachments.md b/docs/version/7.3.0/guides/attachments.md
similarity index 100%
rename from docs/guides/attachments.md
rename to docs/version/7.3.0/guides/attachments.md
diff --git a/docs/guides/bulk-operations.md b/docs/version/7.3.0/guides/bulk-operations.md
similarity index 100%
rename from docs/guides/bulk-operations.md
rename to docs/version/7.3.0/guides/bulk-operations.md
diff --git a/docs/guides/changes.md b/docs/version/7.3.0/guides/changes.md
similarity index 100%
rename from docs/guides/changes.md
rename to docs/version/7.3.0/guides/changes.md
diff --git a/docs/guides/compact-and-destroy.md b/docs/version/7.3.0/guides/compact-and-destroy.md
similarity index 100%
rename from docs/guides/compact-and-destroy.md
rename to docs/version/7.3.0/guides/compact-and-destroy.md
diff --git a/docs/guides/conflicts.md b/docs/version/7.3.0/guides/conflicts.md
similarity index 100%
rename from docs/guides/conflicts.md
rename to docs/version/7.3.0/guides/conflicts.md
diff --git a/docs/guides/databases.md b/docs/version/7.3.0/guides/databases.md
similarity index 100%
rename from docs/guides/databases.md
rename to docs/version/7.3.0/guides/databases.md
diff --git a/docs/guides/documents.md b/docs/version/7.3.0/guides/documents.md
similarity index 100%
rename from docs/guides/documents.md
rename to docs/version/7.3.0/guides/documents.md
diff --git a/docs/guides/guides.json b/docs/version/7.3.0/guides/guides.json
similarity index 100%
rename from docs/guides/guides.json
rename to docs/version/7.3.0/guides/guides.json
diff --git a/docs/guides/index.md b/docs/version/7.3.0/guides/index.md
similarity index 100%
rename from docs/guides/index.md
rename to docs/version/7.3.0/guides/index.md
diff --git a/docs/guides/local-documents.md b/docs/version/7.3.0/guides/local-documents.md
similarity index 100%
rename from docs/guides/local-documents.md
rename to docs/version/7.3.0/guides/local-documents.md
diff --git a/docs/guides/mango-queries.md b/docs/version/7.3.0/guides/mango-queries.md
similarity index 100%
rename from docs/guides/mango-queries.md
rename to docs/version/7.3.0/guides/mango-queries.md
diff --git a/docs/guides/queries.md b/docs/version/7.3.0/guides/queries.md
similarity index 100%
rename from docs/guides/queries.md
rename to docs/version/7.3.0/guides/queries.md
diff --git a/docs/guides/replication.md b/docs/version/7.3.0/guides/replication.md
similarity index 100%
rename from docs/guides/replication.md
rename to docs/version/7.3.0/guides/replication.md
diff --git a/docs/guides/setup-couchdb.md b/docs/version/7.3.0/guides/setup-couchdb.md
similarity index 100%
rename from docs/guides/setup-couchdb.md
rename to docs/version/7.3.0/guides/setup-couchdb.md
diff --git a/docs/guides/setup-pouchdb.md b/docs/version/7.3.0/guides/setup-pouchdb.md
similarity index 100%
rename from docs/guides/setup-pouchdb.md
rename to docs/version/7.3.0/guides/setup-pouchdb.md
diff --git a/docs/guides/updating-deleting.md b/docs/version/7.3.0/guides/updating-deleting.md
similarity index 100%
rename from docs/guides/updating-deleting.md
rename to docs/version/7.3.0/guides/updating-deleting.md
diff --git a/docs/learn.md b/docs/version/7.3.0/learn.md
similarity index 99%
rename from docs/learn.md
rename to docs/version/7.3.0/learn.md
index 0b6e4a3409..d23ac36335 100644
--- a/docs/learn.md
+++ b/docs/version/7.3.0/learn.md
@@ -1,7 +1,7 @@
---
layout: 2ColLeft.html
title: About PouchDB
-sidebar: nav.html
+sidebar: ./nav.html
---
PouchDB is an **in-browser database** that allows applications to save data locally, so that users can enjoy all the features of an app even when they're offline. Plus, the data is synchronized between clients, so users can stay up-to-date wherever they go.
diff --git a/docs/offline.md b/docs/version/7.3.0/offline.md
similarity index 100%
rename from docs/offline.md
rename to docs/version/7.3.0/offline.md
diff --git a/docs/users.md b/docs/version/7.3.0/users.md
similarity index 99%
rename from docs/users.md
rename to docs/version/7.3.0/users.md
index 5b147c3bee..b9aed7570a 100644
--- a/docs/users.md
+++ b/docs/version/7.3.0/users.md
@@ -1,7 +1,7 @@
---
layout: 2ColLeft.html
title: Who's using PouchDB?
-sidebar: nav.html
+sidebar: ./nav.html
---
A list of known products and services that are using PouchDB.
diff --git a/docs/version/8.0.0/adapters.md b/docs/version/8.0.0/adapters.md
new file mode 100644
index 0000000000..cf09c5dd44
--- /dev/null
+++ b/docs/version/8.0.0/adapters.md
@@ -0,0 +1,220 @@
+---
+layout: 2ColLeft.html
+title: Adapters
+sidebar: ./nav.html
+---
+
+PouchDB is not a self-contained database; it is a CouchDB-style abstraction layer over other databases. By default, PouchDB ships with the [IndexedDB][] adapter for the browser, and a [LevelDB][] adapter in Node.js. This can be visualized as so:
+
+
+
+PouchDB attempts to provide a consistent API that "just works" across every browser and JavaScript environment, and in most cases, you can just use the defaults. However, if you're trying to reach the widest possible audience, or if you want the best performance, then you will sometimes want to tinker with the adapter settings.
+
+#### Topics:
+* [PouchDB in the browser](#pouchdb_in_the_browser)
+* [PouchDB in Node.js](#pouchdb_in_node_js)
+* [PouchDB over HTTP](#pouchdb_over_http)
+* [More resources](#more_resources)
+
+
+{% include anchor.html title="PouchDB in the browser" hash="pouchdb_in_the_browser"%}
+
+In the browser, PouchDB prefers IndexedDB.
+
+{% include alert/start.html variant="info"%}
+Prior to PouchDB 7.0.0, the WebSQL adapter was used for Safari/iOS. The WebSQL adapter no longer ships in PouchDB, but may be installed separately.
+{% include alert/end.html%}
+
+If you're ever curious which adapter is being used in a particular browser, you can use the following method:
+
+```js
+const pouch = new PouchDB('myDB');
+console.log(pouch.adapter); // prints 'idb'
+```
+
+### SQLite plugin for Cordova/PhoneGap
+
+On Cordova/PhoneGap/Ionic, the native SQLite database is often a popular choice, because it allows unlimited storage (compared to [IndexedDB/WebSQL storage limits](http://www.html5rocks.com/en/tutorials/offline/quota-research)). It also offers more flexibility in backing up and pre-loading databases, because the SQLite files are directly accessible to app developers.
+
+There are various Cordova plugins that can provide access to native SQLite, such as
+[Cordova-sqlite-storage](https://github.com/litehelpers/Cordova-sqlite-storage),
+[cordova-plugin-sqlite-2](https://github.com/nolanlawson/cordova-plugin-sqlite-2), or
+[cordova-plugin-websql](https://github.com/Microsoft/cordova-plugin-websql).
+
+To use them, you must install them separately into your Cordova application, and then add a special third-party PouchDB adapter
+called [pouchdb-adapter-cordova-sqlite](https://github.com/nolanlawson/pouchdb-adapter-cordova-sqlite). Once you do
+that, you can use it via:
+
+```js
+const db = new PouchDB('myDB.db', {adapter: 'cordova-sqlite'});
+```
+
+{% include alert/start.html variant="info"%}
+In PouchDB pre-6.0.0, Cordova SQLite support was available out-of-the-box, but it has been moved to a separate plugin
+to reduce confusion and to make it explicit whether you are using WebSQL or Cordova SQLite.
+{% include alert/end.html%}
+
+We recommend avoiding Cordova SQLite unless you are hitting the 50MB storage limit in iOS, you
+require native or preloaded access to the database files, or there's some other reason to go native.
+The built-in IndexedDB adapter is nearly always more performant and stable.
+
+### Browser adapter plugins
+
+PouchDB also offers separate browser plugins that use backends other than IndexedDB. These plugins fully pass the PouchDB test suite and are rigorously tested in our CI process.
+
+**Downloads:**
+
+* [pouchdb.memory.js](https://github.com/apache/pouchdb/releases/download/{{ site.version }}/pouchdb.memory.js) (Minified: [pouchdb.memory.min.js](https://github.com/apache/pouchdb/releases/download/{{ site.version }}/pouchdb.memory.min.js))
+* [pouchdb.localstorage.js](https://github.com/apache/pouchdb/releases/download/{{ site.version }}/pouchdb.localstorage.js) (Minified: [pouchdb.localstorage.min.js](https://github.com/apache/pouchdb/releases/download/{{ site.version }}/pouchdb.localstorage.min.js))
+
+{% include alert/start.html variant="warning"%}
+{% markdown %}
+These plugins add a hefty footprint due to external dependencies, so take them with a grain of salt.
+{% endmarkdown %}
+{% include alert/end.html%}
+
+#### In-memory adapter
+
+If you want a quick database for your unit tests, you can use the `pouchdb.memory.js` plugin, which offers a pure in-memory PouchDB:
+
+```html
+
+
+
+```
+
+This pouch will act exactly like a normal one – replicating, storing attachments, pagination, etc. – but it will be deleted as soon as the user closes their browser. However, multiple `PouchDB` objects with the same database name will share the same data:
+
+```js
+// pouch1 and pouch2 will share the same data
+const pouch1 = new PouchDB('myDB', {adapter: 'memory'});
+const pouch2 = new PouchDB('myDB', {adapter: 'memory'});
+
+// pouch3 will have its own data
+const pouch3 = new PouchDB('myOtherDB', {adapter: 'memory'});
+```
+
+#### LocalStorage adapter
+
+If you need to support very old browsers, such as IE ≤ 9.0 and Opera Mini, you can use the `pouchdb.localstorage.js` plugin, which allows PouchDB to fall back to [LocalStorage][] on browsers that don't support either IndexedDB or WebSQL. The [es5-shims][] will also be necessary.
+
+```html
+
+
+
+```
+
+{% include alert/start.html variant="warning"%}
+The LocalStorage plugin should be considered highly experimental, and the underlying structure may change in the future. Currently it stores all document IDs in memory, which works fine on small databases but may crash on larger databases. You can follow localstorage-down to track our progress.
+{% include alert/end.html %}
+
+{% include anchor.html title="PouchDB in Node.js" hash="pouchdb_in_node_js"%}
+
+#### In-memory
+
+{% include alert/start.html variant="warning"%}
+{% markdown %}
+**Warning: deprecation notice.** The `memory` adapter will be deprecated in PouchDB version 10.0.0 and removed in version 11.0.0. You can read [the migration guide here](https://pouchdb.com/2026/04/10/migration-to-nodesqlite.html) and more about the topic in [this link](https://github.com/apache/pouchdb/issues/9163).
+{% endmarkdown %}
+{% include alert/end.html%}
+
+Just as in the browser, you can also create a pure in-memory PouchDB:
+
+```bash
+$ npm install pouchdb-adapter-memory
+```
+
+then:
+
+```js
+PouchDB.plugin(require('pouchdb-adapter-memory'));
+const pouch = new PouchDB('myDB', {adapter: 'memory'});
+```
+
+This implementation is based on [MemDOWN](https://github.com/level/memdown), and will not write any changes to disk.
+
+#### Node SQLite adapter
+
+You can also use PouchDB in Node.js' [native SQLite module](https://nodejs.org/api/sqlite.html), when using Node.js' `>22.5.0` version.
+
+```js
+const PouchDB = require('pouchdb');
+PouchDB.plugin(require('pouchdb-adapter-node-sqlite'));
+
+const db = new PouchDB('mydatabase.db', {adapter: 'nodesqlite'});
+```
+
+#### Other LevelDOWN adapters
+
+{% include alert/start.html variant="warning"%}
+{% markdown %}
+**Warning: deprecation notice.** The `leveldb` adapter will be deprecated in PouchDB version 10.0.0 and removed in version 11.0.0. You can read [the migration guide here](https://pouchdb.com/2026/04/10/migration-to-nodesqlite.html) and more about the topic in [this link](https://github.com/apache/pouchdb/issues/9163).
+{% endmarkdown %}
+{% include alert/end.html%}
+
+Technically you are free to use
+[any LevelDOWN-based implementation](https://github.com/rvagg/node-levelup/wiki/Modules#storage-back-ends) in either Node or the browser.
+However this should be considered **extremely experimental** and not designed for production use.
+
+See [pouchdb-adapter-leveldb-core](https://www.npmjs.com/package/pouchdb-adapter-leveldb-core) for details.
+
+{% include anchor.html title="PouchDB over HTTP" hash="pouchdb_over_http"%}
+
+In both the browser and in Node.js, PouchDB can also function as a straightforward API on top of any [CouchDB](https://couchdb.apache.org/)-compliant database:
+
+```js
+const pouch = new PouchDB('http://my-site.com:5984/my-db');
+const securePouch = new PouchDB('https://my-secure-site.com:5984/my-secure-db');
+```
+
+You can also sync to and from these databases to your local PouchDB.
+
+Currently PouchDB has full support for:
+
+* CouchDB 1.x
+* [Smileupps](https://www.smileupps.com/) (same as 1.x)
+* CouchDB 2.x ([tested in CI](https://github.com/apache/pouchdb/actions))
+* CouchDB 3.x ([tested in CI](https://github.com/apache/pouchdb/actions))
+* [Cloudant](https://cloudant.com/) (roughly the same as 2.x)
+* [PouchDB Server](https://github.com/pouchdb/pouchdb-server) ([tested in CI](https://github.com/apache/pouchdb/actions))
+* [PouchDB Server --in-memory mode](https://github.com/pouchdb/pouchdb-server)
+
+[Drupal 8](http://wearepropeople.com/blog/a-content-staging-solution-for-drupal-8-and-more) has also announced support for PouchDB, and there is [rcouch](https://github.com/rcouch/rcouch) as well, but these are both untested by PouchDB.
+
+If you are ever unsure about a server, consider replicating from PouchDB to CouchDB, then from that CouchDB to the other server.
+
+#### PouchDB Server
+
+[PouchDB Server](https://github.com/pouchdb/pouchdb-server) is a standalone REST server that implements the CouchDB API, while using a LevelDB-based PouchDB under the hood. It also supports an `--in-memory` mode and any [LevelDOWN][] adapter, which you may find handy.
+
+PouchDB Server passes the PouchDB test suite at 100%, but be aware that it is not as full-featured or battle-tested as CouchDB.
+
+#### PouchDB Express
+
+The underlying module for PouchDB Server, [Express PouchDB](https://github.com/pouchdb/express-pouchdb) is an Express submodule that mimics most of the CouchDB API within your Express application.
+
+{% include anchor.html title="More resources" hash="more_resources"%}
+
+The best place to look for information on which browsers support which databases is [caniuse.com](http://caniuse.com). You can consult their tables on browser support for various backends:
+
+* [IndexedDB](http://caniuse.com/indexeddb)
+* [WebSQL](http://caniuse.com/sql-storage)
+* [LocalStorage](http://caniuse.com/namevalue-storage)
+
+[IndexedDB]: http://www.w3.org/TR/IndexedDB/
+[WebSQL]: http://www.w3.org/TR/webdatabase/
+[LevelDB]: https://code.google.com/p/leveldb/
+[LocalStorage]: https://developer.mozilla.org/en-US/docs/Web/Guide/API/DOM/Storage
+[es5-shims]: https://github.com/es-shims/es5-shim
+[sqlite plugin]: https://github.com/brodysoft/Cordova-SQLitePlugin
+[sqlite plugin 2]: https://github.com/nolanlawson/cordova-plugin-sqlite-2
+[leveldown]: https://github.com/rvagg/node-leveldown
+[level-js]: https://github.com/maxogden/level.js
diff --git a/docs/version/8.0.0/api.html b/docs/version/8.0.0/api.html
new file mode 100644
index 0000000000..e7d2e9d777
--- /dev/null
+++ b/docs/version/8.0.0/api.html
@@ -0,0 +1,42 @@
+---
+layout: 2ColLeft.html
+title: API Reference
+sidebar: api.html
+edit: false
+---
+
+{% markdown %}
+
+{% include ./api/overview.html %}
+{% include ./api/create_database.html %}
+{% include ./api/delete_database.html %}
+{% include ./api/create_document.html %}
+{% include ./api/fetch_document.html %}
+{% include ./api/delete_document.html %}
+{% include ./api/batch_create.html %}
+{% include ./api/batch_fetch.html %}
+{% include ./api/changes.html %}
+{% include ./api/replication.html %}
+{% include ./api/sync.html %}
+{% include ./api/save_attachment.html %}
+{% include ./api/get_attachment.html %}
+{% include ./api/delete_attachment.html %}
+{% include ./api/create_index.html %}
+{% include ./api/query_index.html %}
+{% include ./api/explain_index.html %}
+{% include ./api/list_indexes.html %}
+{% include ./api/delete_index.html %}
+{% include ./api/query_database.html %}
+{% include ./api/view_cleanup.html %}
+{% include ./api/database_information.html %}
+{% include ./api/compaction.html %}
+{% include ./api/revisions_diff.html %}
+{% include ./api/bulk_get.html %}
+{% include ./api/close_database.html %}
+{% include ./api/purge.html %}
+{% include ./api/events.html %}
+{% include ./api/active_tasks.html %}
+{% include ./api/defaults.html %}
+{% include ./api/plugins.html %}
+
+{% endmarkdown %}
diff --git a/docs/version/8.0.0/api/active_tasks.html b/docs/version/8.0.0/api/active_tasks.html
new file mode 100644
index 0000000000..673a8a8850
--- /dev/null
+++ b/docs/version/8.0.0/api/active_tasks.html
@@ -0,0 +1,45 @@
+{% include anchor.html edit="true" title="List active tasks" hash="active_tasks" %}
+
+{% highlight js %}
+PouchDB.activeTasks.list()
+{% endhighlight %}
+
+List all active database tasks. There are three types of internal tasks: `database_compaction`, `view_indexing`, and `replication`. PouchDB will report progress of these tasks to the active tasks API and remove tasks as soon as they are completed or have failed.
+
+#### Example Usage:
+
+{% highlight js %}
+const tasks = PouchDB.activeTasks.list()
+{% endhighlight %}
+
+#### Example Result:
+
+{% highlight js %}
+[{
+ "id": "d81fea92-8ce4-42df-bb2b-89a4e67536c3",
+ "name": "database_compaction",
+ "created_at": "2022-02-08T15:38:45.318Z",
+ "total_items": 12,
+ "completed_items": 1,
+ "updated_at": "2022-02-08T15:38:45.821Z"
+}]
+{% endhighlight %}
+
+### Real-time updates
+
+You can use [JavaScript Proxies](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy) to monitor calls to the active tasks API. For the `PouchDB.activeTasks.add()` function, which is used internally to announce new tasks to PouchDB, you can monitor calls as follows:
+
+{% highlight js %}
+PouchDB.activeTasks.add = new Proxy(PouchDB.activeTasks.add, {
+ apply: (target, thisArg, argumentsList) => {
+ const task = argumentsList[0];
+ const id = Reflect.apply(
+ target,
+ PouchDB.activeTasks,
+ [task]
+ );
+ console.log('Added task', id, task.name);
+ return id;
+ },
+});
+{% endhighlight %}
diff --git a/docs/version/8.0.0/api/batch_create.html b/docs/version/8.0.0/api/batch_create.html
new file mode 100644
index 0000000000..13f58a6abf
--- /dev/null
+++ b/docs/version/8.0.0/api/batch_create.html
@@ -0,0 +1,271 @@
+{% include anchor.html edit="true" title="Create/update a batch of documents" hash="batch_create" %}
+
+{% highlight js %}
+db.bulkDocs(docs, [options], [callback])
+{% endhighlight %}
+
+Create, update or delete multiple documents. The `docs` argument is an array of documents.
+
+If you omit an `_id` parameter on a given document, the database will create a new document and assign the ID for you. To update a document, you must include both an `_id` parameter and a `_rev` parameter, which should match the ID and revision of the document on which to base your updates. Finally, to delete a document, include a `_deleted` parameter with the value `true`.
+
+#### Example Usage:
+
+Put some new docs, providing the `_id`s:
+
+{% include code/start.html id="bulk_docs_1" type="callback" %}
+{% highlight js %}
+db.bulkDocs([
+ {title : 'Lisa Says', _id: 'doc1'},
+ {title : 'Space Oddity', _id: 'doc2'}
+], function(err, response) {
+ if (err) { return console.log(err); }
+ // handle result
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="bulk_docs_1" type="async" %}
+{% highlight js %}
+try {
+ const result = await db.bulkDocs([
+ {title : 'Lisa Says', _id: 'doc1'},
+ {title : 'Space Oddity', _id: 'doc2'}
+ ]);
+} catch (err) {
+ console.log(err);
+}
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="bulk_docs_1" type="promise" %}
+{% highlight js %}
+db.bulkDocs([
+ {title : 'Lisa Says', _id: 'doc1'},
+ {title : 'Space Oddity', _id: 'doc2'}
+]).then(function (result) {
+ // handle result
+}).catch(function (err) {
+ console.log(err);
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+Post some new docs and auto-generate the `_id`s:
+
+{% include code/start.html id="bulk_docs_2" type="callback" %}
+{% highlight js %}
+db.bulkDocs([
+ {title : 'Lisa Says'},
+ {title : 'Space Oddity'}
+], function(err, response) {
+ if (err) { return console.log(err); }
+ // handle result
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="bulk_docs_2" type="async" %}
+{% highlight js %}
+try {
+ const result = await db.bulkDocs([
+ {title : 'Lisa Says'},
+ {title : 'Space Oddity'}
+ ]);
+} catch (err) {
+ console.log(err);
+}
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="bulk_docs_2" type="promise" %}
+{% highlight js %}
+db.bulkDocs([
+ {title : 'Lisa Says'},
+ {title : 'Space Oddity'}
+]).then(function (result) {
+ // handle result
+}).catch(function (err) {
+ console.log(err);
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+#### Example Response:
+{% highlight js %}
+[
+ {
+ "ok": true,
+ "id": "doc1",
+ "rev": "1-84abc2a942007bee7cf55007cba56198"
+ },
+ {
+ "ok": true,
+ "id": "doc2",
+ "rev": "1-7b80fc50b6af7a905f368670429a757e"
+ }
+]
+{% endhighlight %}
+
+The response contains an array of the familiar `ok`/`rev`/`id`
+from the [put()/post() API](#create_document). If there are any errors, they
+will be provided individually like so:
+
+{% highlight js %}
+[
+ { status: 409,
+ name: 'conflict',
+ message: 'Document update conflict',
+ error: true
+ }
+]
+{% endhighlight %}
+
+The results are returned in the same order as the supplied "docs" array.
+
+Note that `bulkDocs()` is not transactional, and that you may get
+back a mixed array of errors/non-errors. In CouchDB/PouchDB, the smallest
+atomic unit is the document.
+
+#### Bulk update/delete:
+
+You can also use `bulkDocs()` to update/delete many documents at once:
+
+{% include code/start.html id="bulk_docs3" type="callback" %}
+{% highlight js %}
+db.bulkDocs([
+ {
+ title : 'Lisa Says',
+ artist : 'Velvet Underground',
+ _id : "doc1",
+ _rev : "1-84abc2a942007bee7cf55007cba56198"
+ },
+ {
+ title : 'Space Oddity',
+ artist : 'David Bowie',
+ _id : "doc2",
+ _rev : "1-7b80fc50b6af7a905f368670429a757e"
+ }
+], function(err, response) {
+ if (err) { return console.log(err); }
+ // handle result
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="bulk_docs3" type="async" %}
+{% highlight js %}
+try {
+ const result = await db.bulkDocs([
+ {
+ title : 'Lisa Says',
+ artist : 'Velvet Underground',
+ _id : "doc1",
+ _rev : "1-84abc2a942007bee7cf55007cba56198"
+ },
+ {
+ title : 'Space Oddity',
+ artist : 'David Bowie',
+ _id : "doc2",
+ _rev : "1-7b80fc50b6af7a905f368670429a757e"
+ }
+ ]);
+} catch (err) {
+ console.log(err);
+}
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="bulk_docs3" type="promise" %}
+{% highlight js %}
+db.bulkDocs([
+ {
+ title : 'Lisa Says',
+ artist : 'Velvet Underground',
+ _id : "doc1",
+ _rev : "1-84abc2a942007bee7cf55007cba56198"
+ },
+ {
+ title : 'Space Oddity',
+ artist : 'David Bowie',
+ _id : "doc2",
+ _rev : "1-7b80fc50b6af7a905f368670429a757e"
+ }
+]).then(function (result) {
+ // handle result
+}).catch(function (err) {
+ console.log(err);
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+Or delete them:
+
+{% include code/start.html id="bulk_docs_4" type="callback" %}
+{% highlight js %}
+db.bulkDocs([
+ {
+ title : 'Lisa Says',
+ _deleted : true,
+ _id : "doc1",
+ _rev : "1-84abc2a942007bee7cf55007cba56198"
+ },
+ {
+ title : 'Space Oddity',
+ _deleted : true,
+ _id : "doc2",
+ _rev : "1-7b80fc50b6af7a905f368670429a757e"
+ }
+], function(err, response) {
+ if (err) { return console.log(err); }
+ // handle result
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="bulk_docs_4" type="async" %}
+{% highlight js %}
+try {
+ const result = await db.bulkDocs([
+ {
+ title : 'Lisa Says',
+ _deleted : true,
+ _id : "doc1",
+ _rev : "1-84abc2a942007bee7cf55007cba56198"
+ },
+ {
+ title : 'Space Oddity',
+ _deleted : true,
+ _id : "doc2",
+ _rev : "1-7b80fc50b6af7a905f368670429a757e"
+ }
+ ]);
+} catch (err) {
+ console.log(err);
+}
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="bulk_docs_4" type="promise" %}
+{% highlight js %}
+db.bulkDocs([
+ {
+ title : 'Lisa Says',
+ _deleted : true,
+ _id : "doc1",
+ _rev : "1-84abc2a942007bee7cf55007cba56198"
+ },
+ {
+ title : 'Space Oddity',
+ _deleted : true,
+ _id : "doc2",
+ _rev : "1-7b80fc50b6af7a905f368670429a757e"
+ }
+]).then(function (result) {
+ // handle result
+}).catch(function (err) {
+ console.log(err);
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+**Note:** If you set a `new_edits` property on the options object to `false`, you can post existing documents from other databases without having new revision IDs assigned to them. Normally, only the [replication algorithm](https://docs.couchdb.org/en/stable/replication/protocol.html?highlight=new_edits#upload-batch-of-changed-documents) needs to do this.
diff --git a/docs/version/8.0.0/api/batch_fetch.html b/docs/version/8.0.0/api/batch_fetch.html
new file mode 100644
index 0000000000..0e7e6749ae
--- /dev/null
+++ b/docs/version/8.0.0/api/batch_fetch.html
@@ -0,0 +1,205 @@
+{% include anchor.html edit="true" title="Fetch a batch of documents" hash="batch_fetch" %}
+
+{% highlight js %}
+db.allDocs([options], [callback])
+{% endhighlight %}
+
+Fetch multiple documents, indexed and sorted by the `_id`. Deleted documents are only included if `options.keys` is specified.
+
+### Options
+
+All options default to `false` unless otherwise specified.
+
+* `options.include_docs`: Include the document itself in each row in the `doc` field. Otherwise by default you only get the `_id` and `_rev` properties.
+* `options.conflicts`: Include conflict information in the `_conflicts` field of a doc.
+* `options.attachments`: Include attachment data as base64-encoded string.
+* `options.binary`: Return attachment data as Blobs/Buffers, instead of as base64-encoded strings.
+* `options.startkey` & `options.endkey`: Get documents with IDs in a certain range (inclusive/inclusive).
+* `options.inclusive_end`: Include documents having an ID equal to the given `options.endkey`. Default: `true`.
+* `options.limit`: Maximum number of documents to return.
+* `options.skip`: Number of docs to skip before returning (warning: poor performance on IndexedDB/LevelDB!).
+* `options.descending`: Reverse the order of the output documents. Note that the order of `startkey` and `endkey` is reversed when `descending`:`true`.
+* `options.key`: Only return documents with IDs matching this string key.
+* `options.keys`: Array of string keys to fetch in a single shot.
+ - Neither `startkey` nor `endkey` can be specified with this option.
+ - The rows are returned in the same order as the supplied `keys` array.
+ - The row for a deleted document will have the revision ID of the deletion, and an extra key `"deleted":true` in the `value` property.
+ - The row for a nonexistent document will just contain an `"error"` property with the value `"not_found"`.
+ - For details, see the [CouchDB query options documentation](https://docs.couchdb.org/en/stable/api/ddoc/views.html#db-design-design-doc-view-view-name).
+* `options.update_seq`: Include an `update_seq` value indicating which sequence id of the underlying database the view reflects.
+
+**Notes:** For pagination, `options.limit` and `options.skip` are also available, but the same performance concerns as in CouchDB apply. Use the [startkey/endkey pattern](https://docs.couchdb.org/en/stable/couchapp/views/pagination.html) instead.
+
+#### Example Usage:
+
+{% include code/start.html id="all_docs" type="callback" %}
+{% highlight js %}
+db.allDocs({
+ include_docs: true,
+ attachments: true
+}, function(err, response) {
+ if (err) { return console.log(err); }
+ // handle result
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="all_docs" type="async" %}
+{% highlight js %}
+try {
+ const result = await db.allDocs({
+ include_docs: true,
+ attachments: true
+ });
+} catch (err) {
+ console.log(err);
+}
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="all_docs" type="promise" %}
+{% highlight js %}
+db.allDocs({
+ include_docs: true,
+ attachments: true
+}).then(function (result) {
+ // handle result
+}).catch(function (err) {
+ console.log(err);
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+#### Example Response:
+
+{% highlight js %}
+{
+ "offset": 0,
+ "total_rows": 1,
+ "rows": [{
+ "doc": {
+ "_id": "0B3358C1-BA4B-4186-8795-9024203EB7DD",
+ "_rev": "1-5782E71F1E4BF698FA3793D9D5A96393",
+ "title": "Sound and Vision",
+ "_attachments": {
+ "attachment/its-id": {
+ "content_type": "image/jpg",
+ "data": "R0lGODlhAQABAIAAAP7//wAAACH5BAAAAAAALAAAAAABAAEAAAICRAEAOw==",
+ "digest": "md5-57e396baedfe1a034590339082b9abce"
+ }
+ }
+ },
+ "id": "0B3358C1-BA4B-4186-8795-9024203EB7DD",
+ "key": "0B3358C1-BA4B-4186-8795-9024203EB7DD",
+ "value": {
+ "rev": "1-5782E71F1E4BF698FA3793D9D5A96393"
+ }
+ }]
+}
+{% endhighlight %}
+
+In the response, you have three things:
+
+* `total_rows` the total number of non-deleted documents in the database
+* `offset` the `skip` if provided, or in CouchDB the actual offset
+* `rows`: rows containing the documents, or just the `_id`/`_revs` if you didn't set `include_docs` to `true`.
+
+* You may optionally also have `update_seq` if you set `update_seq` to `true`
+
+You can use `startkey`/`endkey` to find all docs in a range:
+
+{% include code/start.html id="all_docs_2" type="callback" %}
+{% highlight js %}
+db.allDocs({
+ include_docs: true,
+ attachments: true,
+ startkey: 'bar',
+ endkey: 'quux'
+}, function(err, response) {
+ if (err) { return console.log(err); }
+ // handle result
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="all_docs_2" type="async" %}
+{% highlight js %}
+try {
+ const result = await db.allDocs({
+ include_docs: true,
+ attachments: true,
+ startkey: 'bar',
+ endkey: 'quux'
+ });
+} catch (err) {
+ console.log(err);
+}
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="all_docs_2" type="promise" %}
+{% highlight js %}
+db.allDocs({
+ include_docs: true,
+ attachments: true,
+ startkey: 'bar',
+ endkey: 'quux'
+}).then(function (result) {
+ // handle result
+}).catch(function (err) {
+ console.log(err);
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+This will return all docs with `_id`s between `'bar'` and `'quux'`.
+
+#### Prefix search
+
+You can do prefix search in `allDocs()` – i.e. "give me all the documents whose `_id`s start with `'foo'`" – by using the special high Unicode character `'\ufff0'`:
+
+{% include code/start.html id="all_docs_3" type="callback" %}
+{% highlight js %}
+db.allDocs({
+ include_docs: true,
+ attachments: true,
+ startkey: 'foo',
+ endkey: 'foo\ufff0'
+}, function(err, response) {
+ if (err) { return console.log(err); }
+ // handle result
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="all_docs_3" type="async" %}
+{% highlight js %}
+try {
+ const result = await db.allDocs({
+ include_docs: true,
+ attachments: true,
+ startkey: 'foo',
+ endkey: 'foo\ufff0'
+ });
+} catch (err) {
+ console.log(err);
+}
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="all_docs_3" type="promise" %}
+{% highlight js %}
+db.allDocs({
+ include_docs: true,
+ attachments: true,
+ startkey: 'foo',
+ endkey: 'foo\ufff0'
+}).then(function (result) {
+ // handle result
+}).catch(function (err) {
+ console.log(err);
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+This works because CouchDB/PouchDB `_id`s are sorted [lexicographically](https://docs.couchdb.org/en/stable/couchapp/views/collation.html).
diff --git a/docs/version/8.0.0/api/bulk_get.html b/docs/version/8.0.0/api/bulk_get.html
new file mode 100644
index 0000000000..a664a3e1b3
--- /dev/null
+++ b/docs/version/8.0.0/api/bulk_get.html
@@ -0,0 +1,118 @@
+{% include anchor.html edit="true" title="Document bulk get" hash="bulk_get" %}
+
+{% highlight js %}
+db.bulkGet(options, [callback])
+{% endhighlight %}
+
+Given a set of document/revision IDs, returns the document bodies (and, optionally, attachment data) for each ID/revision pair specified.
+
+### Options
+
+* `options.docs`: An array of `id` and `rev` pairs representing the revisions to fetch.
+ - `id`: ID of the document to fetch.
+ - `rev`: Revision of the document to fetch. If this is not specified, all available revisions are fetched.
+ - `atts_since`: Optional and supported by the http adapter only. Includes attachments only since specified revisions. Doesn’t includes attachments for specified revisions.
+* `options.revs`: Each returned revision body will include its revision history as a `_revisions` property. Default is `false`.
+* `options.attachments`: Include attachment data in the response. Default is `false`, resulting in only stubs being returned.
+* `options.binary`: Return attachment data as Blobs/Buffers, instead of as base64-encoded strings. Default is `false`.
+
+
+
+#### Example Usage:
+
+{% include code/start.html id="bulkget1" type="callback" %}
+{% highlight js %}
+db.bulkGet({
+ docs: [
+ { id: "existing-doc", rev: "1-b2e54331db828310f3c772d6e042ac9c"},
+ { id: "foo", rev: "2-3a24009a9525bde9e4bfa8a99046b00d"},
+ { id: "bar", rev: "1-3a24009a9525bde9e4bfa8a99046b00d"}
+ ]
+}, function (err, result) {
+ if (err) { return console.log(err); }
+ // handle result
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="bulkget1" type="async" %}
+{% highlight js %}
+try {
+ const result = await db.bulkGet({
+ docs: [
+ { id: "doc-that-exists", rev: "1-967a00dff5e02add41819138abb3284d"},
+ { id: "doc-that-does-not-exist", rev: "1-3a24009a9525bde9e4bfa8a99046b00d"},
+ { id: "doc-that-exists", rev: "1-bad_rev"}
+ ]
+ });
+} catch (err) {
+ console.log(err);
+}
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="bulkget1" type="promise" %}
+{% highlight js %}
+db.bulkGet({
+ docs: [
+ { id: "doc-that-exists", rev: "1-967a00dff5e02add41819138abb3284d"},
+ { id: "doc-that-does-not-exist", rev: "1-3a24009a9525bde9e4bfa8a99046b00d"},
+ { id: "doc-that-exists", rev: "1-bad_rev"}
+ ]
+}).then(function (result) {
+ // handle result
+}).catch(function (err) {
+ console.log(err);
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+#### Example Response:
+{% highlight js %}
+{
+ "results": [
+ {
+ "docs": [
+ {
+ "ok": {
+ "_id": "doc-that-exists",
+ "_rev": "1-967a00dff5e02add41819138abb3284d",
+ "_revisions": {
+ "ids": [
+ "967a00dff5e02add41819138abb3284d"
+ ],
+ "start": 1
+ }
+ }
+ }],
+ "id": "doc-that-exists"
+ },
+ {
+ "docs": [
+ {
+ "error": {
+ "error": "not_found",
+ "id": "doc-that-does-not-exist",
+ "reason": "missing",
+ "rev": "undefined"
+ }
+ }],
+ "id": "doc-that-does-not-exist"
+ },
+ {
+ "docs": [
+ {
+ "error": {
+ "error": "not_found",
+ "id": "doc-that-exists",
+ "reason": "missing",
+ "rev": "1-badrev"
+ }
+ }
+ ],
+ "id": "doc-that-exists"
+ }]
+}
+{% endhighlight %}
+
+
diff --git a/docs/version/8.0.0/api/changes.html b/docs/version/8.0.0/api/changes.html
new file mode 100644
index 0000000000..6bfbf69079
--- /dev/null
+++ b/docs/version/8.0.0/api/changes.html
@@ -0,0 +1,358 @@
+{% include anchor.html edit="true" title="Listen to database changes" hash="changes" %}
+
+{% highlight js %}
+db.changes(options)
+{% endhighlight %}
+
+A list of changes made to documents in the database, in the order they were made.
+It returns an object with the method `cancel()`, which you call if you don't want to listen to new changes anymore.
+
+It is an [event emitter][event emitter] and will emit a `'change'` event on each document change, a `'complete'` event when all the changes have been processed, and an `'error'` event when an error occurs. Calling `cancel()` will unsubscribe all event listeners automatically.
+
+### Options
+
+All options default to `false` unless otherwise specified.
+
+* `options.live`: Will emit change events for all future changes until cancelled.
+* `options.include_docs`: Include the associated document with each change.
+ * `options.conflicts`: Include conflicts.
+ * `options.attachments`: Include attachments.
+ - `options.binary`: Return attachment data as Blobs/Buffers, instead of as base64-encoded strings.
+* `options.descending`: Reverse the order of the output documents.
+* `options.since`: Start the results from the change immediately after the given sequence number. You can also pass `'now'` if you want only new changes (when `live` is `true`). Ignored if `descending` is true.
+* `options.limit`: Limit the number of results to this number.
+* `options.timeout`: Request timeout (in milliseconds), use `false` to disable.
+* `options.heartbeat`: For http adapter only, time in milliseconds for server to give a heartbeat to keep long connections open. Defaults to 10000 (10 seconds), use `false` to disable the default.
+
+**Filtering Options:**
+
+* `options.filter`: Reference a filter function from a design document to selectively get updates. To use a view function, pass `_view` here and provide a reference to the view function in `options.view`. See [filtered changes](#filtered-changes) for details.
+* `options.doc_ids`: Only show changes for docs with these ids (array of strings).
+* `options.query_params`: Object containing properties that are passed to the filter function, e.g. `{"foo:"bar"}`, where `"bar"` will be available in the filter function as `params.query.foo`. To access the `params`, define your filter function like `function (doc, params) {/* ... */}`.
+* `options.view`: Specify a view function (e.g. `'design_doc_name/view_name'` or `'view_name'` as shorthand for `'view_name/view_name'`) to act as a filter. Documents counted as "passed" for a view filter if a map function emits at least one record for them. **Note**: `options.filter` must be set to `'_view'` for this option to work.
+* `options.selector`: Filter using a query/pouchdb-find [selector](https://docs.couchdb.org/en/stable/api/database/find.html#find-selectors). **Note**: Cannot be used in combination with the filter option.
+
+**Advanced Options:**
+
+* `options.return_docs`: Defaults to `true` except when `options.live = true` then it defaults to `false`. Passing `false` prevents the changes feed from keeping all the documents in memory – in other words complete always has an empty results array, and the `change` event is the only way to get the event. Useful for large change sets where otherwise you would run out of memory.
+* `options.batch_size`: Only available for http databases, this configures how many changes to fetch at a time. Increasing this can reduce the number of requests made. Default is 25.
+* `options.style`: Specifies how many revisions are returned in the changes array. The default, `'main_only'`, will only return the current "winning" revision; `'all_docs'` will return all leaf revisions (including conflicts and deleted former conflicts). Most likely you won't need this unless you're writing a replicator.
+* `options.seq_interval`: Only available for http databases. Specifies that seq information only be generated every N changes. Larger values can improve changes throughput with CouchDB 2.0 and later. Note that `last_seq` is always populated regardless.
+
+#### Example Usage:
+
+{% highlight js %}
+const changes = db.changes({
+ since: 'now',
+ live: true,
+ include_docs: true
+}).on('change', function(change) {
+ // handle change
+}).on('complete', function(info) {
+ // changes() was canceled
+}).on('error', function (err) {
+ console.log(err);
+});
+
+changes.cancel(); // whenever you want to cancel
+{% endhighlight %}
+
+#### Example Response:
+{% highlight js %}
+{
+ "id":"somestuff",
+ "seq":21,
+ "changes":[{
+ "rev":"1-8e6e4c0beac3ec54b27d1df75c7183a8"
+ }],
+ "doc":{
+ "title":"Ch-Ch-Ch-Ch-Changes",
+ "_id":"someDocId",
+ "_rev":"1-8e6e4c0beac3ec54b27d1df75c7183a8"
+ }
+}
+{% endhighlight %}
+
+#### Change events
+
+* __`change`__ (`info`) - This event fires when a change has been found. `info` will contain details about the change, such as whether it was deleted and what the new `_rev` is. `info.doc` will contain the doc if you set `include_docs` to `true`. See below for an example response.
+* __`complete`__ (`info`) - This event fires when all changes have been read. In live changes, only cancelling the changes should trigger this event. `info.results` will contain the list of changes. See below for an example.
+* __`error`__ (`err`) - This event is fired when the changes feed is stopped due to an unrecoverable failure.
+
+#### Example response
+
+Example response in the `'change'` listener (using `{include_docs: true}`):
+
+{% highlight js %}
+{ id: 'doc1',
+ changes: [ { rev: '1-9152679630cc461b9477792d93b83eae' } ],
+ doc: {
+ _id: 'doc1',
+ _rev: '1-9152679630cc461b9477792d93b83eae'
+ },
+ seq: 1
+}
+{% endhighlight %}
+
+Example response in the `'change'` listener when a doc was deleted:
+
+{% highlight js %}
+{ id: 'doc2',
+ changes: [ { rev: '2-9b50a4b63008378e8d0718a9ad05c7af' } ],
+ doc: { _id: 'doc2',
+ _rev: '2-9b50a4b63008378e8d0718a9ad05c7af',
+ _deleted: true
+ },
+ deleted: true,
+ seq: 3
+}
+{% endhighlight %}
+
+Example response in the `'complete'` listener:
+
+{% highlight js %}
+{
+ "results": [
+ {
+ "id": "doc1",
+ "changes": [ { "rev": "1-9152679630cc461b9477792d93b83eae" } ],
+ "doc": {
+ "_id": "doc1",
+ "_rev": "1-9152679630cc461b9477792d93b83eae"
+ },
+ "seq": 1
+ },
+ {
+ "id": "doc2",
+ "changes": [ { "rev": "2-9b50a4b63008378e8d0718a9ad05c7af" } ],
+ "doc": {
+ "_id": "doc2",
+ "_rev": "2-9b50a4b63008378e8d0718a9ad05c7af",
+ "_deleted": true
+ },
+ "deleted": true,
+ "seq": 3
+ }
+ ],
+ "last_seq": 3
+}
+{% endhighlight %}
+
+`seq` and `last_seq` correspond to the overall sequence number of the entire database, and it's what is passed in when using `since` (except for the special `'now'`). It is the primary key for the changes feed, and is also used as a checkpointer by the replication algorithm.
+
+#### Single-shot
+
+If you don't specify `{live: true}`, then you can also use `changes()` in the standard
+callback/promise style, and it will be treated as a single-shot request, which
+returns a list of the changes (i.e. what the `'complete'` event emits):
+
+{% include code/start.html id="changes1" type="callback" %}
+{% highlight js %}
+db.changes({
+ limit: 10,
+ since: 0
+}, function (err, response) {
+ if (err) { return console.log(err); }
+ // handle result
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="changes1" type="async" %}
+{% highlight js %}
+try {
+ const result = await db.changes({
+ limit: 10,
+ since: 0
+ });
+} catch (err) {
+ console.log(err);
+}
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="changes1" type="promise" %}
+{% highlight js %}
+db.changes({
+ limit: 10,
+ since: 0
+}).then(function (result) {
+ // handle result
+}).catch(function (err) {
+ console.log(err);
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+#### Example Response:
+
+{% highlight js %}
+{
+ "results": [{
+ "id": "0B3358C1-BA4B-4186-8795-9024203EB7DD",
+ "seq": 1,
+ "changes": [{
+ "rev": "1-5782E71F1E4BF698FA3793D9D5A96393"
+ }]
+ }, {
+ "id": "mydoc",
+ "seq": 2,
+ "changes": [{
+ "rev": "1-A6157A5EA545C99B00FF904EEF05FD9F"
+ }]
+ }, {
+ "id": "otherdoc",
+ "seq": 3,
+ "changes": [{
+ "rev": "1-3753476B70A49EA4D8C9039E7B04254C"
+ }]
+ }, {
+ "id": "828124B9-3973-4AF3-9DFD-A94CE4544005",
+ "seq": 4,
+ "changes": [{
+ "rev": "1-A8BC08745E62E58830CA066D99E5F457"
+ }]
+ }],
+ "last_seq": 4
+}
+{% endhighlight %}
+
+When `live` is `false`, the returned object is also an event emitter as well as a promise,
+and will fire the `'complete'` event when the results are ready.
+
+Note that this `'complete'` event only fires when you aren't doing live changes.
+
+#### Filtered changes
+
+As with [replicate()](#replication), you can filter using:
+
+* an ad-hoc `filter` function
+* an array of `doc_ids`
+* a `filter` function inside of a design document
+* a `filter` function inside of a design document, with `query_params`
+* a `view` function inside of a design document
+
+If you are running `changes()` on a remote CouchDB, then the first method will run client-side, whereas the last four will filter on the server side. Therefore the last four should be preferred, especially if the database is large, because you want to send as few documents over the wire as possible.
+
+If you are running `changes()` on a local PouchDB, then obviously all five methods will run client-side. There are also no performance benefits to using any of the five, so can also just filter yourself, in your own `on('change')` handler. These methods are implemented in PouchDB purely for consistency with CouchDB.
+
+The named functions can either be specified with `'designdoc_id/function_name'` or (if both design doc id and function name are equal) as `'fname'` as shorthand for `'fname/fname'`.
+
+#### Filtering examples
+
+In these examples, we'll work with some mammals. Let's imagine our docs are:
+
+{% highlight js %}
+[
+ {_id: 'a', name: 'Kangaroo', type: 'marsupial'},
+ {_id: 'b', name: 'Koala', type: 'marsupial'},
+ {_id: 'c', name: 'Platypus', type: 'monotreme'}
+]
+{% endhighlight %}
+
+Here are 5 examples using the 5 different systems.
+
+**Example 1: Ad-hoc `filter` function**
+
+*Warning*: this runs client-side, if the database is remote.
+
+Filter by `type === 'marsupial'`:
+
+{% highlight js %}
+db.changes({
+ filter: function (doc) {
+ return doc.type === 'marsupial';
+ }
+});
+{% endhighlight %}
+
+**Example 2: Array of `doc_ids`**
+
+Filter documents with `_id`s `['a', 'c']`.
+
+{% highlight js %}
+db.changes({
+ doc_ids: ['a', 'c']
+});
+{% endhighlight %}
+
+**Example 3: `filter` function inside of a design document**
+
+First `put()` a design document:
+
+{% highlight js %}
+{
+ _id: '_design/mydesign',
+ filters: {
+ myfilter: function (doc) {
+ return doc.type === 'marsupial';
+ }.toString()
+ }
+}
+{% endhighlight %}
+
+Then filter by `type === 'marsupial'`:
+
+{% highlight js %}
+db.changes({
+ filter: 'mydesign/myfilter'
+});
+{% endhighlight %}
+
+**Example 4: `filter` function inside of a design document, with `query_params`**
+
+This is the most powerful way to filter, because it allows you to pass in arbitrary options to your filter function.
+
+First `put()` a design document:
+
+{% highlight js %}
+{
+ _id: '_design/myfilter',
+ filters: {
+ myfilter: function (doc, req) {
+ return doc.type === req.query.type;
+ }.toString()
+ }
+}
+{% endhighlight %}
+
+Then filter by `type === 'marsupial'`:
+
+{% highlight js %}
+db.changes({
+ filter: 'myfilter',
+ query_params: {type: 'marsupial'}
+});
+{% endhighlight %}
+
+Since both the design document and the filter function have the same name, we can shorten the function name to `'myfilter'`.
+
+**Example 5: `view` function inside of a design document**
+
+This doesn't really offer any advantages compared to the previous two methods, unless you are already using a `view` for map/reduce queries, and you want to reuse it.
+
+Any documents that `emit()` anything will be considered to have passed this filter method.
+
+First `put()` a design document:
+
+{% highlight js %}
+{
+ _id: '_design/mydesign',
+ views: {
+ myview: function (doc) {
+ if (doc.type === 'marsupial') {
+ emit(doc._id);
+ }
+ }.toString()
+ }
+}
+{% endhighlight %}
+
+Then filter by `type === 'marsupial'`:
+
+{% highlight js %}
+db.changes({
+ filter: '_view',
+ view: 'mydesign/myview'
+});
+{% endhighlight %}
diff --git a/docs/version/8.0.0/api/close_database.html b/docs/version/8.0.0/api/close_database.html
new file mode 100644
index 0000000000..3e82f1563e
--- /dev/null
+++ b/docs/version/8.0.0/api/close_database.html
@@ -0,0 +1,33 @@
+{% include anchor.html edit="true" title="Close a database" hash="close_database"%}
+
+{% highlight js %}
+db.close([callback])
+{% endhighlight %}
+
+Close the database, this closes any open connection to the underlying storage and frees memory (event listeners) the database may be using.
+
+#### Example Usage
+
+{% include code/start.html id="close_db" type="callback" %}
+{% highlight js %}
+db.close(function () {
+ // success
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="close_db" type="async" %}
+
+{% highlight js %}
+await db.close();
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="close_db" type="promise" %}
+
+{% highlight js %}
+db.close().then(function () {
+ // success
+});
+{% endhighlight %}
+{% include code/end.html %}
diff --git a/docs/version/8.0.0/api/compaction.html b/docs/version/8.0.0/api/compaction.html
new file mode 100644
index 0000000000..c936375d54
--- /dev/null
+++ b/docs/version/8.0.0/api/compaction.html
@@ -0,0 +1,49 @@
+{% include anchor.html edit="true" title="Compact the database" hash="compaction" %}
+
+{% highlight js %}
+db.compact([options], [callback])
+{% endhighlight %}
+
+Triggers a compaction operation in the local or remote database. This reduces the database's size by removing unused and old data, namely non-leaf revisions and attachments that are no longer referenced by those revisions. Note that this is a separate operation from [`viewCleanup()`](#view_cleanup).
+
+For remote databases, PouchDB checks the compaction status at regular intervals and fires the callback (or resolves the promise) upon completion. Consult the [compaction section of CouchDB's maintenance documentation](http://couchdb.readthedocs.org/en/latest/maintenance/compaction.html) for more details.
+
+Also see [auto-compaction](#create_database), which runs compaction automatically (local databases only).
+
+* `options.interval`: Number of milliseconds to wait before asking again if compaction is already done. Defaults to 200. (Only applies to remote databases.)
+
+#### Example Usage:
+
+{% include code/start.html id="compact" type="callback" %}
+{% highlight js %}
+db.compact(function (err, result) {
+ if (err) { return console.log(err); }
+ // handle result
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="compact" type="async" %}
+{% highlight js %}
+try {
+ const result = await db.compact();
+} catch (err) {
+ console.log(err);
+}
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="compact" type="promise" %}
+{% highlight js %}
+db.compact().then(function (result) {
+ // handle result
+}).catch(function (err) {
+ console.log(err);
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+#### Example Response:
+{% highlight js %}
+{ "ok" : "true" }
+{% endhighlight %}
diff --git a/docs/version/8.0.0/api/create_database.html b/docs/version/8.0.0/api/create_database.html
new file mode 100644
index 0000000000..d2792ca0fc
--- /dev/null
+++ b/docs/version/8.0.0/api/create_database.html
@@ -0,0 +1,64 @@
+{% include anchor.html edit="true" title="Create a database" hash="create_database" %}
+
+{% highlight js %}
+new PouchDB([name], [options])
+{% endhighlight %}
+
+This method creates a database or opens an existing one. If you use a URL like `'http://domain.com/dbname'`, then PouchDB will work as a client to an online CouchDB instance. Otherwise it will create a local database using [whatever backend is present](/adapters.html).
+
+### Options
+
+* `name`: You can omit the `name` argument and specify it via `options` instead. Note that the name is required.
+
+**Options for local databases:**
+
+* `auto_compaction`: This turns on auto compaction, which means `compact()` is called after every change to the database. Defaults to `false`.
+* `adapter`: One of `'indexeddb'`, `'idb'`, `'leveldb'`, `'nodesqlite'`, or `'http'`.
+* `revs_limit`: Specify how many old revisions we keep track (not a copy) of. Specifying a low value means Pouch may not be able to figure out whether a new revision received via replication is related to any it currently has which could result in a conflict. Defaults to `1000`.
+* `deterministic_revs`: Use a md5 hash to create a deterministic revision number for documents. Setting it to false will mean that the revision number will be a random UUID. Defaults to true.
+* `view_update_changes_batch_size`: Specify how many change records will be consumed at a time when rebuilding view indexes when the `query()` method is used. Defaults to 50.
+* `view_adapter`: Specify a different adapter to store the view index data.
+* `purged_infos_limit`: Specify how many purged revisions we keep track of. Specifying a low value can result in Pouch not delegating older purges down to views. Defaults to `1000`.
+
+**Options for remote databases:**
+
+* `fetch(url, opts)`: Intercept or override the HTTP request, you can add or modify any headers or options relating to the http request then return a new fetch Promise.
+* `auth.username` + `auth.password`: You can specify HTTP auth parameters either by using a database with a name in the form `http://user:pass@host/name` or via the `auth.username` + `auth.password` options.
+* `skip_setup`: Initially PouchDB checks if the database exists, and tries to create it, if it does not exist yet. Set this to `true` to skip this setup.
+
+**Notes:**
+
+1. In IndexedDB PouchDB will use `_pouch_` to prefix the internal database names. Do not manually create databases with the same prefix.
+2. When acting as a client on Node, any other options given will be passed to [request][].
+3. When using the `'leveldb'` adapter (the default on Node), any other options given will be passed to [levelup][].
+4. If you are using the jwt auth handler, please use the fetch option to add the required headers and handle connected logic like token refreshes.
+
+[request]: https://github.com/mikeal/request
+[levelup]: https://github.com/rvagg/node-levelup
+[levelup_options]: https://github.com/rvagg/node-levelup/#options
+
+#### Example Usage:
+{% highlight js %}
+const db = new PouchDB('dbname');
+// or
+const db = new PouchDB('http://localhost:5984/dbname');
+{% endhighlight %}
+
+Create an in-memory Pouch (must install `pouchdb-adapter-memory` first):
+
+{% highlight js %}
+const db = new PouchDB('dbname', {adapter: 'memory'});
+{% endhighlight %}
+
+Create a remote PouchDB with special fetch options:
+
+{% highlight js %}
+const db = new PouchDB('http://example.com/dbname', {
+ fetch: function (url, opts) {
+ opts.headers.set('X-Some-Special-Header', 'foo');
+ return PouchDB.fetch(url, opts);
+ }
+});
+{% endhighlight %}
+
+For more info, check out [adapters](/adapters.html).
diff --git a/docs/version/8.0.0/api/create_document.html b/docs/version/8.0.0/api/create_document.html
new file mode 100644
index 0000000000..6a377ead31
--- /dev/null
+++ b/docs/version/8.0.0/api/create_document.html
@@ -0,0 +1,174 @@
+{% include anchor.html edit="true" title="Create/update a document" hash="create_document" %}
+
+### Using db.put()
+{% highlight js %}
+db.put(doc, [options], [callback])
+{% endhighlight %}
+
+Create a new document or update an existing document. If the document already exists, you must specify its revision `_rev`, otherwise a conflict will occur.
+
+If you want to update an existing document even if there's conflict, you should specify the base revision `_rev` and use `force=true` option, then a new conflict revision will be created.
+
+`doc` must be a "pure JSON object", i.e. a collection of name/value pairs. If you try to store non-JSON data (for instance `Date` objects) you may see [inconsistent results]({{ site.baseurl }}/errors.html#could_not_be_cloned).
+
+#### Example Usage:
+
+Create a new doc with an `_id` of `'mydoc'`:
+
+{% include code/start.html id="newDoc" type="callback" %}
+{% highlight js %}
+db.put({
+ _id: 'mydoc',
+ title: 'Heroes'
+}, function(err, response) {
+ if (err) { return console.log(err); }
+ // handle response
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="newDoc" type="async" %}
+{% highlight js %}
+try {
+ const response = await db.put({
+ _id: 'mydoc',
+ title: 'Heroes'
+ });
+} catch (err) {
+ console.log(err);
+}
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="newDoc" type="promise" %}
+{% highlight js %}
+db.put({
+ _id: 'mydoc',
+ title: 'Heroes'
+}).then(function (response) {
+ // handle response
+}).catch(function (err) {
+ console.log(err);
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+You can update an existing doc using `_rev`:
+
+{% include code/start.html id="updateDoc" type="callback" %}
+{% highlight js %}
+db.get('mydoc', function(err, doc) {
+ if (err) { return console.log(err); }
+ db.put({
+ _id: 'mydoc',
+ _rev: doc._rev,
+ title: "Let's Dance"
+ }, function(err, response) {
+ if (err) { return console.log(err); }
+ // handle response
+ });
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="updateDoc" type="async" %}
+{% highlight js %}
+try {
+ const doc = await db.get('mydoc');
+ const response = await db.put({
+ _id: 'mydoc',
+ _rev: doc._rev,
+ title: "Let's Dance"
+ });
+} catch (err) {
+ console.log(err);
+}
+{% endhighlight %}
+{% include code/end.html %}
+
+
+{% include code/start.html id="updateDoc" type="promise" %}
+{% highlight js %}
+db.get('mydoc').then(function(doc) {
+ return db.put({
+ _id: 'mydoc',
+ _rev: doc._rev,
+ title: "Let's Dance"
+ });
+}).then(function(response) {
+ // handle response
+}).catch(function (err) {
+ console.log(err);
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+#### Example Response:
+{% highlight js %}
+{
+ "ok": true,
+ "id": "mydoc",
+ "rev": "1-A6157A5EA545C99B00FF904EEF05FD9F"
+}
+{% endhighlight %}
+
+The response contains the `id` of the document, the new `rev`, and an `ok` to reassure
+you that everything is okay.
+
+### Using db.post()
+
+{% highlight js %}
+db.post(doc, [options], [callback])
+{% endhighlight %}
+
+Create a new document and let PouchDB auto-generate an `_id` for it.
+
+#### Example Usage:
+
+{% include code/start.html id="post_doc" type="callback" %}
+{% highlight js %}
+db.post({
+ title: 'Ziggy Stardust'
+}, function (err, response) {
+ if (err) { return console.log(err); }
+ // handle response
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="post_doc" type="async" %}
+{% highlight js %}
+try {
+ const response = await db.post({
+ title: 'Ziggy Stardust'
+ });
+} catch (err) {
+ console.log(err);
+}
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="post_doc" type="promise" %}
+{% highlight js %}
+db.post({
+ title: 'Ziggy Stardust'
+}).then(function (response) {
+ // handle response
+}).catch(function (err) {
+ console.log(err);
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+#### Example Response:
+{% highlight js %}
+{
+ "ok" : true,
+ "id" : "8A2C3761-FFD5-4770-9B8C-38C33CED300A",
+ "rev" : "1-d3a8e0e5aa7c8fff0c376dac2d8a4007"
+}
+{% endhighlight %}
+
+**Put vs. post**: The basic rule of thumb is: `put()` new documents with an `_id`, `post()` new documents without an `_id`.
+
+You should also prefer `put()` to `post()`, because when you `post()`, you are missing an opportunity to use `allDocs()` to sort documents by `_id` (because your `_id`s are random). For more info, read the [PouchDB pro tips](/2014/06/17/12-pro-tips-for-better-code-with-pouchdb.html).
diff --git a/docs/version/8.0.0/api/create_index.html b/docs/version/8.0.0/api/create_index.html
new file mode 100644
index 0000000000..38b26a61f4
--- /dev/null
+++ b/docs/version/8.0.0/api/create_index.html
@@ -0,0 +1,302 @@
+{% include anchor.html edit="true" title="Create index" hash="create_index" %}
+
+{% highlight js %}
+db.createIndex(index [, callback])
+{% endhighlight %}
+
+Create an index if it doesn't exist, or do nothing if it already exists.
+
+{% include alert/start.html variant="info"%}
+{% markdown %}
+
+**pouchdb-find plugin needed:** This API requires the `pouchdb-find` plugin. See
+[Mango queries](/guides/mango-queries.html) for installation instructions.
+
+{% endmarkdown %}
+{% include alert/end.html%}
+
+#### Example Usage:
+
+{% include code/start.html id="create_idx" type="callback" %}
+{% highlight js %}
+db.createIndex({
+ index: {
+ fields: ['foo']
+ }
+}, function (err, result) {
+ if (err) { return console.log(err); }
+ // handle result
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="create_idx" type="async" %}
+{% highlight js %}
+try {
+ const result = await db.createIndex({
+ index: {
+ fields: ['foo']
+ }
+ });
+} catch (err) {
+ console.log(err);
+}
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="create_idx" type="promise" %}
+{% highlight js %}
+db.createIndex({
+ index: {
+ fields: ['foo']
+ }
+}).then(function (result) {
+ // handle result
+}).catch(function (err) {
+ console.log(err);
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+#### Example Response:
+
+If the index was created, you'll see:
+
+{% highlight js %}
+{ "result": "created" }
+{% endhighlight %}
+
+Or if the index already exists:
+
+{% highlight js %}
+{ "result": "exists" }
+{% endhighlight %}
+
+You can also create an index on multiple fields:
+
+{% include code/start.html id="create_idx2" type="callback" %}
+{% highlight js %}
+db.createIndex({
+ index: {
+ fields: ['foo', 'bar', 'baz']
+ }
+}, function (err, result) {
+ if (err) { return console.log(err); }
+ // handle result
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="create_idx2" type="async" %}
+{% highlight js %}
+try {
+ const result = await db.createIndex({
+ index: {
+ fields: ['foo', 'bar', 'baz']
+ }
+ });
+} catch (err) {
+ console.log(err);
+}
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="create_idx2" type="promise" %}
+{% highlight js %}
+db.createIndex({
+ index: {
+ fields: ['foo', 'bar', 'baz']
+ }
+}).then(function (result) {
+ // handle result
+}).catch(function (err) {
+ console.log(err);
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+Or an index on deep fields:
+
+{% include code/start.html id="create_idx3" type="callback" %}
+{% highlight js %}
+db.createIndex({
+ index: {
+ fields: ['person.address.zipcode']
+ }
+}, function (err, result) {
+ if (err) { return console.log(err); }
+ // handle result
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="create_idx3" type="async" %}
+{% highlight js %}
+try {
+ const result = await db.createIndex({
+ index: {
+ fields: ['person.address.zipcode']
+ }
+ });
+} catch (err) {
+ console.log(err);
+}
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="create_idx3" type="promise" %}
+{% highlight js %}
+db.createIndex({
+ index: {
+ fields: ['person.address.zipcode']
+ }
+}).then(function (result) {
+ // handle result
+}).catch(function (err) {
+ console.log(err);
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+You can also specify additional options, if you want more control over how your index is created:
+
+{% include code/start.html id="create_idx4" type="callback" %}
+{% highlight js %}
+db.createIndex({
+ index: {
+ fields: ['foo', 'bar'],
+ name: 'myindex',
+ ddoc: 'mydesigndoc',
+ type: 'json',
+ }
+}, function (err, result) {
+ if (err) { return console.log(err); }
+ // handle result
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="create_idx4" type="async" %}
+{% highlight js %}
+try {
+ const result = await db.createIndex({
+ index: {
+ fields: ['foo', 'bar'],
+ name: 'myindex',
+ ddoc: 'mydesigndoc',
+ type: 'json',
+ }
+ });
+} catch (err) {
+ console.log(err);
+}
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="create_idx4" type="promise" %}
+{% highlight js %}
+db.createIndex({
+ index: {
+ fields: ['foo', 'bar'],
+ name: 'myindex',
+ ddoc: 'mydesigndoc',
+ type: 'json',
+ }
+}).then(function (result) {
+ // handle result
+}).catch(function (err) {
+ console.log(err);
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+If you want to index a subset of the documents in the database, you can use `partial_filter_selector`. This has the same syntax as the selector you'd pass to `find()`, and only documents matching the selector will be included in the index.
+
+{% include alert/start.html variant="warning"%}
+{% markdown %}
+
+**When using _pouchdb-adapter-indexeddb:_** On the `indexeddb`-adapter, indexes with
+a`partial_filter_selector` will fall back to using [map-reduce](#query_database),
+instead of the native indexes, negating all index performance gains from the
+`indexeddb`-adapter over the `idb`-adapter.
+
+{% endmarkdown %}
+{% include alert/end.html%}
+
+{% include code/start.html id="create_idx5" type="callback" %}
+{% highlight js %}
+db.createIndex({
+ index: {
+ fields: ['year', 'title'],
+ partial_filter_selector: {
+ year: { $gt: 2010 }
+ }
+ }
+}, function (err, result) {
+ if (err) { return console.log(err); }
+ // handle result
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="create_idx5" type="async" %}
+{% highlight js %}
+try {
+ const result = db.createIndex({
+ index: {
+ fields: ['year', 'title'],
+ partial_filter_selector: {
+ year: { $gt: 2010 }
+ }
+ }
+ });
+} catch (err) {
+ console.log(err);
+}
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="create_idx5" type="promise" %}
+{% highlight js %}
+db.createIndex({
+ index: {
+ fields: ['year', 'title'],
+ partial_filter_selector: {
+ year: { $gt: 2010 }
+ }
+ }
+}).then(function (result) {
+ // handle result
+}).catch(function (err) {
+ console.log(err);
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+### Options
+
+* `fields`: a list of fields to index
+* `name` (optional): name of the index, auto-generated if you don't include it
+* `ddoc` (optional): design document name (i.e. the part after `'_design/'`), auto-generated if you don't include it
+* `type` (optional): only supports `'json'`, which is also the default
+* `partial_filter_selector` (optional): a _selector_ used to filter the set of documents included in the index
+
+When a PouchDB instance updates an index, it emits `indexing` events that include information about the progress of the index update task.
+
+{% highlight js %}
+const db = new PouchDB('my-docs');
+
+db.on('indexing', function (event) {
+ // called when indexes are updated
+});
+{% endhighlight %}
+
+The `event` object contains the following fields:
+
+* `view`: the name of the view that's being updated
+* `indexed_docs`: the total number of document updates processed during the current run
+
+When an index is updated this event is emitted once at the start of the update, with `indexed_docs` equal to `0`. It will then emit further events as batches of changes are processed, and those events will also include these fields:
+
+* `last_seq`: the `seq` value of the last event from the database change feed that's been processed
+* `results_count`: the number of changes in the most recent batch of changes
diff --git a/docs/version/8.0.0/api/database_information.html b/docs/version/8.0.0/api/database_information.html
new file mode 100644
index 0000000000..e2cb79ff5f
--- /dev/null
+++ b/docs/version/8.0.0/api/database_information.html
@@ -0,0 +1,59 @@
+{% include anchor.html edit="true" title="Get database information" hash="database_information" %}
+
+{% highlight js %}
+db.info([callback])
+{% endhighlight %}
+
+Get information about a database.
+
+#### Example Usage:
+
+{% include code/start.html id="dbinfo" type="callback" %}
+{% highlight js %}
+db.info(function(err, info) {
+ if (err) { return console.log(err); }
+ // handle result
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="dbinfo" type="async" %}
+{% highlight js %}
+try {
+ const result = await db.info();
+} catch (err) {
+ console.log(err);
+}
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="dbinfo" type="promise" %}
+{% highlight js %}
+db.info().then(function (result) {
+ // handle result
+}).catch(function (err) {
+ console.log(err);
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+#### Example Response:
+{% highlight js %}
+{
+ "db_name": "test",
+ "doc_count": 4,
+ "update_seq": 5
+}
+{% endhighlight %}
+
+**Response object:**
+
+* `db_name` is the name of the database you gave when you called `new PouchDB()`, and also the unique identifier for the database.
+* `doc_count` is the total number of non-deleted documents in the database.
+* `update_seq` is the sequence number of the database. It starts at 0 and gets incremented every time a document is added or modified.
+
+There are also some details you can use for debugging. These are unofficial and may change at any time:
+
+* `adapter`: The name of the adapter being used (idb, leveldb, ...).
+* `idb_attachment_format`: (IndexedDB) either `'base64'` or `'binary'`, depending on whether the browser [supports binary blobs](/faq.html#data_types).
+* `backend_adapter`: (Node.JS) the backend *DOWN adapter being used (MemDOWN, RiakDOWN, ...).
diff --git a/docs/version/8.0.0/api/defaults.html b/docs/version/8.0.0/api/defaults.html
new file mode 100644
index 0000000000..de2b22cab5
--- /dev/null
+++ b/docs/version/8.0.0/api/defaults.html
@@ -0,0 +1,42 @@
+{% include anchor.html edit="true" title="Default settings" hash="defaults" %}
+
+If you find yourself using the same constructor options repeatedly,
+you can simplify your code with `PouchDB.defaults()`:
+
+{% highlight js %}
+PouchDB.defaults({
+ option1: 'foo',
+ option2: 'value'
+});
+{% endhighlight %}
+
+The returned object is a constructor function that works the same as `PouchDB`, except that whenever you invoke it (e.g. with `new`), the given options will be passed in by default.
+
+#### Example Usage:
+{% highlight js %}
+const MyMemPouch = PouchDB.defaults({
+ adapter: 'memory'
+});
+// In-memory PouchDB
+const myMemPouch = new MyMemPouch('dbname');
+
+const MyPrefixedPouch = PouchDB.defaults({
+ prefix: '/path/to/my/db/'
+});
+// db will be named '/path/to/my/db/dbname', useful for LevelDB
+const myPrefixedPouch = new MyPrefixedPouch('dbname');
+
+const HTTPPouch = PouchDB.defaults({
+ prefix: 'http://example.org'
+});
+
+// db will be located at 'http://example.org/dbname'
+const myHttpPouch = new HTTPPouch('dbname');
+{% endhighlight %}
+
+Note the special constructor option `prefix`, which appends a prefix to the database name
+and can be helpful for URL-based or file-based LevelDOWN path names.
+
+All [constructor options](#create_database) are supported. Default options can still be overriden individually.
+
+
diff --git a/docs/version/8.0.0/api/delete_attachment.html b/docs/version/8.0.0/api/delete_attachment.html
new file mode 100644
index 0000000000..bceff6a70c
--- /dev/null
+++ b/docs/version/8.0.0/api/delete_attachment.html
@@ -0,0 +1,52 @@
+{% include anchor.html edit="true" title="Delete an attachment" hash="delete_attachment" %}
+
+{% highlight js %}
+db.removeAttachment(docId, attachmentId, rev, [callback])
+{% endhighlight %}
+
+Delete an attachment from a doc. You must supply the `rev` of the existing doc.
+
+#### Example Usage:
+
+{% include code/start.html id="delete_att" type="callback" %}
+{% highlight js %}
+const rev = '1-068E73F5B44FEC987B51354DFC772891';
+db.removeAttachment('doc', 'att.txt', rev, function(err, res) {
+ if (err) { return console.log(err); }
+ // handle result
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="delete_att" type="async" %}
+{% highlight js %}
+try {
+ const rev = '1-068E73F5B44FEC987B51354DFC772891';
+ const result = await db.removeAttachment('doc', 'att.txt', rev);
+} catch (err) {
+ console.log(err);
+}
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="delete_att" type="promise" %}
+{% highlight js %}
+const rev = '1-068E73F5B44FEC987B51354DFC772891';
+db.removeAttachment('doc', 'att.txt', rev).then(function (result) {
+ // handle result
+}).catch(function (err) {
+ console.log(err);
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+#### Example Response:
+
+{% highlight js %}
+{
+ "ok": true,
+ "rev": "2-1F983211AB87EFCCC980974DFC27382F"
+}
+{% endhighlight %}
+
+
diff --git a/docs/version/8.0.0/api/delete_database.html b/docs/version/8.0.0/api/delete_database.html
new file mode 100644
index 0000000000..69cca3cad8
--- /dev/null
+++ b/docs/version/8.0.0/api/delete_database.html
@@ -0,0 +1,50 @@
+{% include anchor.html edit="true" title="Delete a database" hash="delete_database"%}
+
+{% highlight js %}
+db.destroy([options], [callback])
+{% endhighlight %}
+
+Delete the database. Note that this has no impact on other replicated databases.
+
+#### Example Usage
+
+{% include code/start.html id="destroy_db" type="callback" %}
+{% highlight js %}
+db.destroy(function (err, response) {
+ if (err) {
+ return console.log(err);
+ } else {
+ // success
+ }
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="destroy_db" type="async" %}
+
+{% highlight js %}
+try {
+ await db.destroy();
+} catch (err) {
+ console.log(err);
+}
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="destroy_db" type="promise" %}
+
+{% highlight js %}
+db.destroy().then(function (response) {
+ // success
+}).catch(function (err) {
+ console.log(err);
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+#### Example Response:
+{% highlight js %}
+{
+ "ok" : true
+}
+{% endhighlight %}
diff --git a/docs/version/8.0.0/api/delete_document.html b/docs/version/8.0.0/api/delete_document.html
new file mode 100644
index 0000000000..c6e11c7c15
--- /dev/null
+++ b/docs/version/8.0.0/api/delete_document.html
@@ -0,0 +1,140 @@
+{% include anchor.html edit="true" title="Delete a document" hash="delete_document"%}
+
+{% highlight js %}
+db.remove(doc, [options], [callback])
+{% endhighlight %}
+
+Or:
+
+{% highlight js %}
+db.remove(docId, docRev, [options], [callback])
+{% endhighlight %}
+
+
+Deletes the document. `doc` is required to be a document with at least an `_id` and a `_rev` property. Sending the full document will work as well.
+
+See [filtered replication]({{ site.baseurl }}/api.html#filtered-replication) for why you might want to use `put()` with `{_deleted: true}` instead.
+
+#### Example Usage:
+
+{% include code/start.html id="delete_doc" type="callback" %}
+{% highlight js %}
+db.get('mydoc', function(err, doc) {
+ if (err) { return console.log(err); }
+ db.remove(doc, function(err, response) {
+ if (err) { return console.log(err); }
+ // handle response
+ });
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="delete_doc" type="async" %}
+{% highlight js %}
+try {
+ const doc = await db.get('mydoc');
+ const response = await db.remove(doc);
+} catch (err) {
+ console.log(err);
+}
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="delete_doc" type="promise" %}
+{% highlight js %}
+db.get('mydoc').then(function(doc) {
+ return db.remove(doc);
+}).then(function (result) {
+ // handle result
+}).catch(function (err) {
+ console.log(err);
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+#### Example Response:
+
+{% highlight js %}
+{
+ "ok": true,
+ "id": "mydoc",
+ "rev": "2-9AF304BE281790604D1D8A4B0F4C9ADB"
+}
+{% endhighlight %}
+
+You can also delete a document by just providing an `id` and `rev`:
+
+{% include code/start.html id="delete_doc3" type="callback" %}
+{% highlight js %}
+db.get('mydoc', function(err, doc) {
+ if (err) { return console.log(err); }
+ db.remove(doc._id, doc._rev, function(err, response) {
+ if (err) { return console.log(err); }
+ // handle response
+ });
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="delete_doc3" type="async" %}
+{% highlight js %}
+try {
+ const doc = await db.get('mydoc');
+ const response = await db.remove(doc._id, doc._rev);
+} catch (err) {
+ console.log(err);
+}
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="delete_doc3" type="promise" %}
+{% highlight js %}
+db.get('mydoc').then(function(doc) {
+ return db.remove(doc._id, doc._rev);
+}).then(function (result) {
+ // handle result
+}).catch(function (err) {
+ console.log(err);
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+You can also delete a document by using `put()` with `{_deleted: true}`:
+
+{% include code/start.html id="delete_doc2" type="callback" %}
+{% highlight js %}
+db.get('mydoc', function(err, doc) {
+ if (err) { return console.log(err); }
+ doc._deleted = true;
+ db.put(doc, function(err, response) {
+ if (err) { return console.log(err); }
+ // handle response
+ });
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="delete_doc2" type="async" %}
+{% highlight js %}
+try {
+ const doc = await db.get('mydoc');
+ doc._deleted = true;
+ const result = await db.put(doc);
+} catch (err) {
+ console.log(err);
+}
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="delete_doc2" type="promise" %}
+{% highlight js %}
+db.get('mydoc').then(function(doc) {
+ doc._deleted = true;
+ return db.put(doc);
+}).then(function (result) {
+ // handle result
+}).catch(function (err) {
+ console.log(err);
+});
+{% endhighlight %}
+{% include code/end.html %}
diff --git a/docs/version/8.0.0/api/delete_index.html b/docs/version/8.0.0/api/delete_index.html
new file mode 100644
index 0000000000..733bd743c1
--- /dev/null
+++ b/docs/version/8.0.0/api/delete_index.html
@@ -0,0 +1,129 @@
+{% include anchor.html edit="true" title="Delete index" hash="delete_index" %}
+
+{% highlight js %}
+db.deleteIndex(index [, callback])
+{% endhighlight %}
+
+Delete an index, remove any orphaned design documents, and clean up any leftover data on disk.
+
+{% include alert/start.html variant="info"%}
+{% markdown %}
+
+**pouchdb-find plugin needed:** This API requires the `pouchdb-find` plugin. See
+[Mango queries](/guides/mango-queries.html) for installation instructions.
+
+{% endmarkdown %}
+{% include alert/end.html%}
+
+
+#### Example Usage:
+
+{% include code/start.html id="delete_idx" type="callback" %}
+{% highlight js %}
+db.deleteIndex({
+ "ddoc": "_design/idx-0f3a6f73110868266fa5c688caf8acd3",
+ "name": "idx-0f3a6f73110868266fa5c688caf8acd3",
+ "type": "json",
+ "def": {
+ "fields": [
+ { "foo": "asc" },
+ { "bar": "asc" }
+ ]
+ }
+}, function (err, result) {
+ if (err) { return console.log(err); }
+ // handle result
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="delete_idx" type="async" %}
+{% highlight js %}
+try {
+ const result = await db.deleteIndex({
+ "ddoc": "_design/idx-0f3a6f73110868266fa5c688caf8acd3",
+ "name": "idx-0f3a6f73110868266fa5c688caf8acd3",
+ "type": "json",
+ "def": {
+ "fields": [
+ { "foo": "asc" },
+ { "bar": "asc" }
+ ]
+ }
+ });
+} catch (err) {
+ console.log(err);
+}
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="delete_idx" type="promise" %}
+{% highlight js %}
+db.deleteIndex({
+ "ddoc": "_design/idx-0f3a6f73110868266fa5c688caf8acd3",
+ "name": "idx-0f3a6f73110868266fa5c688caf8acd3",
+ "type": "json",
+ "def": {
+ "fields": [
+ { "foo": "asc" },
+ { "bar": "asc" }
+ ]
+ }
+}).then(function (result) {
+ // handle result
+}).catch(function (err) {
+ console.log(err);
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+#### Example Response:
+
+{% highlight js %}
+{ "ok": true }
+{% endhighlight %}
+
+Note that the easiest way to do this is to locate the index you want to delete using [`getIndexes()`](#list_indexes).
+For instance, here is how you would delete the second index from that list (which should be the
+one after the built-in `_all_docs` index):
+
+{% include code/start.html id="delete_idx2" type="callback" %}
+{% highlight js %}
+db.getIndexes(function (err, indexesResult) {
+ if (err) { return console.log(err); }
+ db.deleteIndex(indexesResult.indexes[1], function (err, result) {
+ if (err) { return console.log(err); }
+ // handle result
+ });
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="delete_idx2" type="async" %}
+{% highlight js %}
+try {
+ const indexesResult = await db.getIndexes();
+ const result = await db.deleteIndex(indexesResult.indexes[1]);
+} catch (err) {
+ console.log(err);
+}
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="delete_idx2" type="promise" %}
+{% highlight js %}
+db.getIndexes().then(function (indexesResult) {
+ return db.deleteIndex(indexesResult.indexes[1]);
+}).then(function (result) {
+ // handle result
+}).catch(function (err) {
+ console.log(err);
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+**Notes:**
+
+* You don't need to provide a `_rev` when deleting an index.
+* The associated design doc is automatically deleted, assuming it only contains one index.
+* There is no need to call [`viewCleanup`](#view_cleanup) to clean up any leftover data. `deleteIndex()` does this automatically for you.
diff --git a/docs/version/8.0.0/api/events.html b/docs/version/8.0.0/api/events.html
new file mode 100644
index 0000000000..1e7563e305
--- /dev/null
+++ b/docs/version/8.0.0/api/events.html
@@ -0,0 +1,14 @@
+{% include anchor.html edit="true" title="Events" hash="events" %}
+
+PouchDB is an [event emitter][event emitter] and will emit a `'created'` event when a database is created. A `'destroyed'` event is emitted when a database is destroyed.
+
+{% highlight js %}
+PouchDB.on('created', function (dbName) {
+ // called whenever a db is created.
+});
+PouchDB.on('destroyed', function (dbName) {
+ // called whenever a db is destroyed.
+});
+{% endhighlight %}
+
+
diff --git a/docs/version/8.0.0/api/explain_index.html b/docs/version/8.0.0/api/explain_index.html
new file mode 100644
index 0000000000..e79cd50e2c
--- /dev/null
+++ b/docs/version/8.0.0/api/explain_index.html
@@ -0,0 +1,135 @@
+{% include anchor.html edit="true" title="Explain index" hash="explain_index" %}
+
+{% highlight js %}
+db.explain(request [, callback])
+{% endhighlight %}
+
+Explain the query plan for a given query
+
+{% include alert/start.html variant="info"%}
+{% markdown %}
+
+**pouchdb-find plugin needed:** This API requires the `pouchdb-find` plugin. See
+[Mango queries](/guides/mango-queries.html) for installation instructions.
+
+{% endmarkdown %}
+{% include alert/end.html%}
+
+#### Example Usage:
+
+{% include code/start.html id="explain_idx" type="callback" %}
+{% highlight js %}
+db.explain({
+ selector: {
+ name: 'Mario',
+ series: "mario"
+ },
+ fields: ['_id', 'name'],
+ sort: ['name']
+}, function (err, explanation) {
+ if (err) { return console.log(err); }
+ // view explanation
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="explain_idx" type="async" %}
+{% highlight js %}
+try {
+ const explanation = await db.explain({
+ selector: {
+ name: 'Mario',
+ series: "mario"
+ },
+ fields: ['_id', 'name'],
+ sort: ['name']
+ });
+} catch (err) {
+ console.log(err);
+}
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="explain_idx" type="promise" %}
+{% highlight js %}
+db.explain({
+ selector: {
+ name: 'Mario',
+ series: "mario"
+ },
+ fields: ['_id', 'name'],
+ sort: ['name']
+}).then(function (explanation) {
+ // view explanation
+}).catch(function (err) {
+ console.log(err);
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+#### Example Response:
+
+{% highlight js %}
+{
+ "dbname": "database-name"
+ "index": {
+ "ddoc": "_design/design-doc-name",
+ "name": "index-name",
+ "type": "json",
+ "def": {
+ "fields": [
+ {
+ "name": "asc"
+ },
+ {
+ "series": "asc"
+ }
+ ]
+ }
+ },
+ "selector": {
+ "$and": [
+ {
+ "name": {
+ "$eq": "mario"
+ }
+ },
+ {
+ "series": {
+ "$eq": "mario"
+ }
+ }
+ ]
+ },
+ "opts": {
+ "use_index": [],
+ "bookmark": "nil",
+ "limit": 25,
+ "skip": 0,
+ "sort": {"name": "asc"},
+ "fields": [
+ "_id"
+ ],
+ "r": [
+ 49
+ ],
+ "conflicts": false
+ },
+ "limit": 10,
+ "skip": 1,
+ "fields": [
+ "_id"
+ ],
+ "range": {
+ "start_key": [
+ "mario",
+ "mario"
+ ],
+ "end_key": [
+ "mario",
+ "mario",
+ {}
+ ]
+ }
+};
+{% endhighlight %}
diff --git a/docs/version/8.0.0/api/fetch_document.html b/docs/version/8.0.0/api/fetch_document.html
new file mode 100644
index 0000000000..0897a24f09
--- /dev/null
+++ b/docs/version/8.0.0/api/fetch_document.html
@@ -0,0 +1,64 @@
+
+{% include anchor.html edit="true" title="Fetch a document" hash="fetch_document"%}
+
+{% highlight js %}
+db.get(docId, [options], [callback])
+{% endhighlight %}
+
+Retrieves a document, specified by `docId`.
+
+### Options
+
+All options default to `false` unless otherwise specified.
+
+* `options.rev`: Fetch specific revision of a document. Defaults to winning revision (see [the CouchDB guide](http://guide.couchdb.org/draft/conflicts.html)).
+* `options.revs`: Include revision history of the document.
+* `options.revs_info`: Include a list of revisions of the document, and their availability.
+* `options.open_revs`: Fetch all leaf revisions if `open_revs="all"` or fetch all leaf revisions specified in `open_revs` array. Leaves will be returned in the same order as specified in input array.
+* `options.conflicts`: If specified, conflicting leaf revisions will be attached in `_conflicts` array.
+* `options.attachments`: Include attachment data.
+ * `options.binary`: Return attachment data as Blobs/Buffers, instead of as base64-encoded strings.
+* `options.latest`: Forces retrieving latest “leaf” revision, no matter what rev was requested. Default is `false`.
+
+#### Example Usage:
+
+{% include code/start.html id="get1" type="callback" %}
+{% highlight js %}
+db.get('mydoc', function(err, doc) {
+ if (err) { return console.log(err); }
+ // handle doc
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="get1" type="async" %}
+{% highlight js %}
+try {
+ const doc = await db.get('mydoc');
+} catch (err) {
+ console.log(err);
+}
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="get1" type="promise" %}
+{% highlight js %}
+db.get('mydoc').then(function (doc) {
+ // handle doc
+}).catch(function (err) {
+ console.log(err);
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+#### Example Response:
+{% highlight js %}
+{
+ "_id": "mydoc",
+ "_rev": "1-A6157A5EA545C99B00FF904EEF05FD9F"
+ "title": "Rock and Roll Heart",
+}
+{% endhighlight %}
+
+The response contains the document as it is stored in the database, along with its
+`_id` and `_rev`.
diff --git a/docs/version/8.0.0/api/get_attachment.html b/docs/version/8.0.0/api/get_attachment.html
new file mode 100644
index 0000000000..5578176228
--- /dev/null
+++ b/docs/version/8.0.0/api/get_attachment.html
@@ -0,0 +1,116 @@
+{% include anchor.html edit="true" title="Get an attachment" hash="get_attachment" %}
+
+{% highlight js %}
+db.getAttachment(docId, attachmentId, [options], [callback])
+{% endhighlight %}
+
+Get attachment data.
+
+### Options
+
+* `options.rev`: as with [get()](#fetch_document), you can pass a `rev` in and get back an attachment for the document at that particular revision.
+
+#### Example Usage:
+
+Get an attachment with filename `'att.txt'` from document with ID `'doc'`:
+
+{% include code/start.html id="get_att1" type="callback" %}
+{% highlight js %}
+db.getAttachment('doc', 'att.txt', function(err, blobOrBuffer) {
+ if (err) { return console.log(err); }
+ // handle result
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="get_att1" type="async" %}
+{% highlight js %}
+try {
+ const blobOrBuffer = await db.getAttachment('doc', 'att.txt');
+} catch (err) {
+ console.log(err);
+}
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="get_att1" type="promise" %}
+{% highlight js %}
+db.getAttachment('doc', 'att.txt').then(function (blobOrBuffer) {
+ // handle result
+}).catch(function (err) {
+ console.log(err);
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+Get an attachment with filename `'att.txt'` from document with ID `'doc'`, at
+the revision `'1-abcd'`:
+
+{% include code/start.html id="get_att2" type="callback" %}
+{% highlight js %}
+db.getAttachment('doc', 'att.txt', {rev: '1-abcd'}, function(err, blobOrBuffer) {
+ if (err) { return console.log(err); }
+ // handle result
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="get_att2" type="async" %}
+{% highlight js %}
+try {
+ const blobOrBuffer = await db.getAttachment('doc', 'att.txt', {rev: '1-abcd'});
+} catch (err) {
+ console.log(err);
+}
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="get_att2" type="promise" %}
+{% highlight js %}
+db.getAttachment('doc', 'att.txt', {rev: '1-abcd'}).then(function (blobOrBuffer) {
+ // handle result
+}).catch(function (err) {
+ console.log(err);
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+#### Response type:
+
+The response will be a `Blob` object in the browser, and a `Buffer` object in Node.js. See [blob-util](https://github.com/nolanlawson/blob-util) for utilities to transform `Blob`s to other formats, such as base64-encoded strings, data URLs, array buffers, etc.
+
+#### Inline base64 attachments
+
+You can specify `{attachments: true}` to most "read" operations, such as `get()`, `allDocs()`, `changes()`, and `query()`. The attachment data will then be included inlined in the resulting doc(s). However, it will always be supplied as base64. For example:
+
+{% highlight js %}
+{
+ "_attachments": {
+ "att.txt": {
+ "content_type": "text/plain",
+ "digest": "d5ccfd24a8748bed4e2c9a279a2b6089",
+ "data": "SXMgdGhlcmUgbGlmZSBvbiBNYXJzPw=="
+ }
+ },
+ "_id": "mydoc",
+ "_rev": "1-e147d9ec9c85139dfe7e93bc17148d1a"
+}
+{% endhighlight %}
+
+For such APIs, when you don't specify `{attachments: true}`, you will instead get metadata about the attachments. For example:
+
+{% highlight js %}
+{
+ "_attachments": {
+ "att.txt": {
+ "content_type": "text/plain",
+ "digest": "d5ccfd24a8748bed4e2c9a279a2b6089",
+ "stub": true
+ }
+ },
+ "_id": "mydoc",
+ "_rev": "1-e147d9ec9c85139dfe7e93bc17148d1a"
+}
+{% endhighlight %}
+
+This "summary" operation may be faster in some cases, because the attachment itself does not need to be read from disk.
\ No newline at end of file
diff --git a/docs/version/8.0.0/api/list_indexes.html b/docs/version/8.0.0/api/list_indexes.html
new file mode 100644
index 0000000000..fa24c9be11
--- /dev/null
+++ b/docs/version/8.0.0/api/list_indexes.html
@@ -0,0 +1,84 @@
+{% include anchor.html edit="true" title="List indexes" hash="list_indexes" %}
+
+{% highlight js %}
+db.getIndexes([callback])
+{% endhighlight %}
+
+Get a list of all the indexes you've created. Also tells you about the
+special `_all_docs` index, i.e. the default index on the `_id` field.
+
+{% include alert/start.html variant="info"%}
+{% markdown %}
+
+**pouchdb-find plugin needed:** This API requires the `pouchdb-find` plugin. See
+[Mango queries](/guides/mango-queries.html) for installation instructions.
+
+{% endmarkdown %}
+{% include alert/end.html%}
+
+#### Example Usage:
+
+{% include code/start.html id="get_idxs" type="callback" %}
+{% highlight js %}
+db.getIndexes(function (err, result) {
+ if (err) { return console.log(err); }
+ // handle result
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="get_idxs" type="async" %}
+{% highlight js %}
+try {
+ const result = await db.getIndexes();
+} catch (err) {
+ console.log(err);
+}
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="get_idxs" type="promise" %}
+{% highlight js %}
+db.getIndexes().then(function (result) {
+ // handle result
+}).catch(function (err) {
+ console.log(err);
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+#### Example Response:
+
+{% highlight js %}
+{
+ "indexes": [
+ {
+ "ddoc": null,
+ "name": "_all_docs",
+ "type": "special",
+ "def": {
+ "fields": [
+ {
+ "_id": "asc"
+ }
+ ]
+ }
+ },
+ {
+ "ddoc": "_design/idx-0f3a6f73110868266fa5c688caf8acd3",
+ "name": "idx-0f3a6f73110868266fa5c688caf8acd3",
+ "type": "json",
+ "def": {
+ "fields": [
+ {
+ "foo": "asc"
+ },
+ {
+ "bar": "asc"
+ }
+ ]
+ }
+ }
+ ]
+}
+{% endhighlight %}
\ No newline at end of file
diff --git a/docs/version/8.0.0/api/overview.html b/docs/version/8.0.0/api/overview.html
new file mode 100644
index 0000000000..02df9305fd
--- /dev/null
+++ b/docs/version/8.0.0/api/overview.html
@@ -0,0 +1,59 @@
+{% include anchor.html edit="true" title="API overview" hash="overview" %}
+
+PouchDB has an asynchronous API, supporting [callbacks](http://docs.nodejitsu.com/articles/getting-started/control-flow/what-are-callbacks), [promises][promise], and
+[async functions](https://jakearchibald.com/2014/es7-async-functions/). For beginners, we recommend promises, although you are free to use whatever format you prefer. If you are unsure, check out our [guide to asynchronous code](/guides/async-code.html).
+
+Most of the API is exposed as:
+
+{% highlight js %}
+db.doSomething(args..., [options], [callback])
+{% endhighlight %}
+
+… where both the `options` and `callback` are optional.
+
+### Callbacks
+
+Callbacks use the standard Node.js idiom of:
+
+{% highlight js %}
+function(error, result) { /* ... */ }
+{% endhighlight %}
+
+… where the `error` will be undefined if there's no error.
+
+### Promises
+
+If you don't specify a `callback`, then the API returns a [promise][]. In [supported browsers](http://caniuse.com/#feat=promises) or Node.js, native promises are used, falling back to the minimal library [lie][] as needed.
+
+{% include alert/start.html variant="info"%}
+{% markdown %}
+
+**Using Ionic/AngularJS?** You can wrap PouchDB promises in [`$q.when()`](https://docs.angularjs.org/api/ng/service/$q#when). This will notify AngularJS to update the UI when the PouchDB promise has resolved.
+
+{% endmarkdown %}
+{% include alert/end.html%}
+
+To use a custom promise implementation with PouchDB, you must redefine a global `Promise` object before loading PouchDB:
+
+{% highlight html %}
+
+
+{% endhighlight %}
+
+### Async functions
+
+Another option is to use `async`/`await` pattern instead of promises chain (ie. `.then().catch()`). `async`/`await` is widely supported by all major browsers and Node.js. Use a transpiler with `async`/`await` polyfill such as [Babel](http://babeljs.io/) if you need to support older browsers.
+
+Note that the samples for `async`/`await` in the API documentation assume that your code is inside an async function. So for instance:
+
+{% highlight js %}
+async function myFunction() {
+ // your code goes in here
+}
+{% endhighlight %}
+
+Any `await` not inside of an async function is a syntax error. For more information about `async`/`await`, read [our introductory blog post]({{ site.baseurl }}/2015/03/05/taming-the-async-beast-with-es7.html).
+
+[promise]: https://www.promisejs.org/
+[lie]: https://github.com/calvinmetcalf/lie
+[event emitter]: http://nodejs.org/api/events.html#events_class_events_eventemitter
diff --git a/docs/version/8.0.0/api/plugins.html b/docs/version/8.0.0/api/plugins.html
new file mode 100644
index 0000000000..06b29c1113
--- /dev/null
+++ b/docs/version/8.0.0/api/plugins.html
@@ -0,0 +1,121 @@
+{% include anchor.html edit="true" title="Plugins" hash="plugins" %}
+
+{% include alert/start.html variant="info"%}
+{% markdown %}
+
+**This section covers _authoring_ plugins.** For a list of third-party plugins,
+see [Plugins](/external.html), or for a list of first-party plugins that you can
+use to customize the PouchDB build, see [Custom Builds](/custom.html).
+
+{% endmarkdown %}
+{% include alert/end.html%}
+
+Writing a plugin is easy! The API is:
+
+{% highlight js %}
+PouchDB.plugin({
+ methodName: myFunction
+});
+{% endhighlight %}
+
+This will add a `db.methodName()` to all databases, which runs `myFunction`.It will always be called in context, so that within the function, `this` refers to the database object.
+
+There is a [PouchDB Plugin Seed project](https://github.com/pouchdb/plugin-seed), which is the fastest way to get started writing, building and testing your very own plugin.
+
+#### Example Usage:
+{% highlight js %}
+PouchDB.plugin({
+ sayHello : function () {
+ console.log("Hello!");
+ }
+});
+new PouchDB('foobar').sayHello(); // prints "Hello!"
+{% endhighlight %}
+
+Alternatively, instead of passing in an object to `.plugin()`, you can pass in
+a function that takes the `PouchDB` object and performs whatever operations
+you want on it. You can use this to load multiple plugins, add adapters,
+or attach event listeners to the `PouchDB` object.
+
+#### Example Usage:
+{% highlight js %}
+PouchDB.plugin(function (PouchDB) {
+ PouchDB.hello = 'world';
+});
+console.log(PouchDB.hello); // prints "world"
+{% endhighlight %}
+
+(Most likely, if you are writing a PouchDB plugin, you will export either the
+object-style or the function-style plugin, so that your users can then
+attach it to their PouchDB object.)
+
+#### Load Plugins from require()
+
+You can load plugins into PouchDB when you load it via `require()`.
+
+{% highlight js %}
+const greet = {sayHello: function() { console.log("Hello!"); }};
+
+const PouchDB = require('pouchdb').plugin(greet);
+
+const db = new PouchDB('foobar');
+db.sayHello(); // prints "Hello!"
+{% endhighlight %}
+
+You can chain plugins, as well:
+
+{% highlight js %}
+const greet = {sayHello: function() { console.log("Hello!"); }};
+const manners = {thank: function(name) { console.log("Thank you, " + name); }};
+
+const PouchDB = require('pouchdb')
+ .plugin(greet)
+ .plugin(manners);
+
+const db = new PouchDB('foobar');
+db.sayHello(); // prints "Hello!"
+db.thank('Mom'); // prints "Thank you, Mom"
+{% endhighlight %}
+
+#### Example Plugin: Intercept Updates
+
+A useful feature of plugins is to intercept updates before they are stored in PouchDB. In this way, a plugin might validate that the data is correct for the application, or even alter documents before they are committed to the database.
+
+The best way to intercept all updates to a PouchDB database is to **override the `bulkDocs()` method**. All changes to PouchDB documents ultimately pass through the `bulkDocs()` method. For example, a call to `put()` will become a `bulkDocs()` call with a "batch" of one document.
+
+Because PouchDB guarantees to plugin authors that all data changes ultimately happen via `bulkDocs()`, it is the ideal place for an application or plugin to intercept updates.
+
+{% highlight js %}
+// Keep a reference to the "upstream" function.
+const pouchBulkDocs = PouchDB.prototype.bulkDocs;
+PouchDB.plugin({bulkDocs: validBulkDocs});
+
+function validBulkDocs(body, options, callback) {
+ if (typeof options == 'function') {
+ callback = options
+ options = {}
+ }
+
+ let docs;
+ if (Array.isArray(body)) {
+ docs = body;
+ } else {
+ docs = body.docs;
+ }
+
+ // All documents must have a .name field.
+ for (let i = 0; i < docs.length; i++) {
+ if (!docs[i].name) {
+ const id = doc._id || '(no _id given)';
+ return callback(new Error('Document is missing .name field: ' + id));
+ }
+ }
+
+ // All documents check out. Pass them to PouchDB.
+ return pouchBulkDocs.call(this, docs, options, callback);
+}
+{% endhighlight %}
+
+The above plugin would return an error if anything ever attempts to store an unnamed document, including documents which change during replication.
+
+Note: this is a very, very simple validation example. It does not behave, for example, like the Apache CouchDB `validate_doc_update()` API.
diff --git a/docs/version/8.0.0/api/purge.html b/docs/version/8.0.0/api/purge.html
new file mode 100644
index 0000000000..54374a8026
--- /dev/null
+++ b/docs/version/8.0.0/api/purge.html
@@ -0,0 +1,79 @@
+{% include anchor.html edit="true" title="Purge a document rev" hash="purge" %}
+
+{% highlight js %}
+db.purge(docId, rev)
+{% endhighlight %}
+
+Purges a specific revision of a document, specified by `docId` and `rev`. `rev` must be a leaf revision.
+
+Purge permanently removes data from the database. Normal deletion with `db.remove()` does not, it only marks the document as `_deleted=true` and creates a new revision. This behaviour ensures that deletes can be replicated across databases, and deleted documents don’t get undeleted by syncing with a database that still has this document.
+
+`db.purge()` is not intended as a regular method for deleting documents, instead, it is meant as an admin function for cases where some secret was erroneously added to the database and must now be removed completely, eg. a credit card or social security number. Purge effectively puts the database in a state where the offending write never happened.
+
+{% include alert/start.html variant="info"%}
+{% markdown %}
+
+**Purge is currently only implemented in the `indexeddb` adapter**.
+
+Using Purge with any other adapter will return an error.
+
+{% endmarkdown %}
+{% include alert/end.html%}
+
+#### Example Usage:
+
+{% include code/start.html id="purge1" type="callback" %}
+{% highlight js %}
+db.purge('mydoc', '6-3a24009a9525bde9e4bfa8a99046b00d',
+ function (err, result) {
+ if (err) { return console.log(err); }
+ // handle result
+);
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="purge1" type="async" %}
+{% highlight js %}
+try {
+ const result = await db.purge('mydoc', '6-3a24009a9525bde9e4bfa8a99046b00d');
+ // handle result
+} catch (err) {
+ console.log(err);
+}
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="purge1" type="promise" %}
+{% highlight js %}
+db.purge('mydoc', '6-3a24009a9525bde9e4bfa8a99046b00d')
+ .then(function (result) {
+ // handle result
+ }).catch(function (err) {
+ console.log(err);
+ });
+{% endhighlight %}
+{% include code/end.html %}
+
+#### Example Response:
+
+{% highlight js %}
+ {
+ "ok": true,
+ "deletedRevs": [
+ "6-3a24009a9525bde9e4bfa8a99046b00d",
+ "5-df4a81cd21c75c71974d96e88a68fc2f"
+ ],
+ "documentWasRemovedCompletely": false
+ }
+{% endhighlight %}
+
+If the document has no conflicts, purging its only leaf rev deletes the document completely, and `purge()` returns `documentWasRemovedCompletely: true` in its result object.
+
+If the document does have conflicts, purging will remove the specified rev and pick a leaf from another rev branch as the winner. Fetching the doc after the purge will return the updated state of the document with the new winning revision in `_rev`.
+
+**Notes:**
+
+* The `rev` specified for purging must be a leaf, otherwise an error will be returned
+* Purges do not sync, they only apply to the database they are performed on
+* If the document has conflicts, purging a leaf will also remove its ancestors up to the next branching rev, and pick a new winning rev
+* Any attachments referenced in the specified rev will also be deleted
diff --git a/docs/version/8.0.0/api/query_database.html b/docs/version/8.0.0/api/query_database.html
new file mode 100644
index 0000000000..073ce32fe0
--- /dev/null
+++ b/docs/version/8.0.0/api/query_database.html
@@ -0,0 +1,459 @@
+{% include anchor.html edit="true" title="Map/reduce queries" hash="query_database" %}
+
+{% highlight js %}
+db.query(fun, [options], [callback])
+{% endhighlight %}
+
+Invoke a map/reduce function, which allows you to perform more complex queries on PouchDB than what you get with [allDocs()](#batch_fetch), [changes()](#changes), or [find()](#query_index). The [CouchDB documentation for map/reduce](https://docs.couchdb.org/en/stable/couchapp/views/intro.html) applies to PouchDB.
+
+Since views perform a full scan of all documents, this method may be slow, unless you first save your view in a design document. Read the [query guide](/guides/queries.html) for a good tutorial.
+
+{% include alert/start.html variant="warning"%}
+{% markdown %}
+
+**Warning: advanced API.** The map/reduce API is designed for cases that are too complex for Mango queries, which are described in
+[createIndex()](#create_index), [find()](#query_index), [listIndexes()](#list_indexes), and [deleteIndex()](#delete_index).
+
+Under the hood, Mango indexes are the same as map/reduce indexes. The Mango API is just a simplified user-facing API on top of map/reduce.
+
+{% endmarkdown %}
+{% include alert/end.html%}
+
+### Options
+
+All options default to `false` unless otherwise specified.
+
+* `fun`: Map/reduce function, which can be one of the following:
+ * A full CouchDB-style map/reduce view: `{map : ..., reduce: ...}`.
+ * A map function by itself (no reduce).
+ * The name of a view in an existing design document (e.g. `'mydesigndoc/myview'`, or `'myview'` as a shorthand for `'myview/myview'`).
+* `options.reduce`: Defaults to `true` when a reduce function is defined, or `false` otherwise. Valid values:
+ * `true` - calls the defined `reduce` function, or the `map` function if no `reduce` is defined.
+ * `false` - calls the defined `map` function.
+ * A reduce function.
+ * The string name of a built-in function: `'_sum'`, `'_count'`, or `'_stats'`.
+ * Tip: if you're not using a built-in, [you're probably doing it wrong](http://youtu.be/BKQ9kXKoHS8?t=865s).
+ * PouchDB will always call your reduce function with rereduce == false. As for CouchDB, refer to the [CouchDB documentation](https://docs.couchdb.org/en/stable/couchapp/views/intro.html).
+* `options.include_docs`: Include the document in each row in the `doc` field.
+ - `options.conflicts`: Include conflicts in the `_conflicts` field of a doc.
+ - `options.attachments`: Include attachment data.
+ - `options.binary`: Return attachment data as Blobs/Buffers, instead of as base64-encoded strings.
+* `options.startkey` & `options.endkey`: Get rows with keys in a certain range (inclusive/inclusive).
+* `options.inclusive_end`: Include rows having a key equal to the given `options.endkey`. Default: `true`.
+* `options.limit`: Maximum number of rows to return.
+* `options.skip`: Number of rows to skip before returning (warning: poor performance on IndexedDB/LevelDB!).
+* `options.descending`: Reverse the order of the output rows.
+* `options.key`: Only return rows matching this key.
+* `options.keys`: Array of keys to fetch in a single shot.
+ - Neither `startkey` nor `endkey` can be specified with this option.
+ - The rows are returned in the same order as the supplied `keys` array.
+ - The row for a deleted document will have the revision ID of the deletion, and an extra key `"deleted":true` in the `value` property.
+ - The row for a nonexistent document will just contain an `"error"` property with the value `"not_found"`.
+* `options.group`: True if you want the reduce function to group results by keys, rather than returning a single result. Defaults to `false`.
+* `options.group_level`: Number of elements in a key to group by, assuming the keys are arrays. Defaults to the full length of the array.
+* `options.stale`: One of `'ok'` or `'update_after'`. Only applies to saved views. Can be one of:
+ * unspecified (default): Returns the latest results, waiting for the view to build if necessary.
+ * `'ok'`: Returns results immediately, even if they're out-of-date.
+ * `'update_after'`: Returns results immediately, but kicks off a build afterwards.
+* `options.update_seq`: Include an `update_seq` value indicating which sequence id of the underlying database the view reflects.
+
+For details, see the [CouchDB query options documentation](https://docs.couchdb.org/en/stable/api/ddoc/views.html#get--db-_design-ddoc-_view-view).
+
+#### Example Usage:
+
+{% include code/start.html id="query1" type="callback" %}
+{% highlight js %}
+// create a design doc
+const ddoc = {
+ _id: '_design/index',
+ views: {
+ index: {
+ map: function mapFun(doc) {
+ if (doc.title) {
+ emit(doc.title);
+ }
+ }.toString()
+ }
+ }
+}
+
+// save the design doc
+db.put(ddoc, function (err) {
+ if (err && err.name !== 'conflict') {
+ return console.log(err);
+ }
+ // ignore if doc already exists
+ // find docs where title === 'Lisa Says'
+ db.query('index', {
+ key: 'Lisa Says',
+ include_docs: true
+ }, function (err, result) {
+ if (err) { return console.log(err); }
+ // handle result
+ });
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="query1" type="async" %}
+{% highlight js %}
+// create a design doc
+const ddoc = {
+ _id: '_design/index',
+ views: {
+ index: {
+ map: function mapFun(doc) {
+ if (doc.title) {
+ emit(doc.title);
+ }
+ }.toString()
+ }
+ }
+}
+
+// save the design doc
+try {
+ try {
+ await db.put(ddoc);
+ } catch (err) {
+ if (err.name !== 'conflict') {
+ throw err;
+ }
+ // ignore if doc already exists
+ }
+ // find docs where title === 'Lisa Says'
+ const result = await db.query('index', {
+ key: 'Lisa Says',
+ include_docs: true
+ });
+} catch (err) {
+ console.log(err);
+}
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="query1" type="promise" %}
+{% highlight js %}
+// create a design doc
+const ddoc = {
+ _id: '_design/index',
+ views: {
+ index: {
+ map: function mapFun(doc) {
+ if (doc.title) {
+ emit(doc.title);
+ }
+ }.toString()
+ }
+ }
+}
+
+// save the design doc
+db.put(ddoc).catch(function (err) {
+ if (err.name !== 'conflict') {
+ throw err;
+ }
+ // ignore if doc already exists
+}).then(function () {
+ // find docs where title === 'Lisa Says'
+ return db.query('index', {
+ key: 'Lisa Says',
+ include_docs: true
+ });
+}).then(function (result) {
+ // handle result
+}).catch(function (err) {
+ console.log(err);
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+#### Example Response:
+{% highlight js %}
+{
+ "offset" : 0,
+ "rows": [{
+ "id": "doc3",
+ "key": "Lisa Says",
+ "value": null,
+ "doc": {
+ "_id": "doc3",
+ "_rev": "1-z",
+ "title": "Lisa Says"
+ }
+ }],
+ "total_rows" : 4
+}
+{% endhighlight %}
+
+In the result,`total_rows` is the total number of possible results in the view. The response is very similar to that of `allDocs()`.
+
+The result may also have `update_seq` if you set `update_seq` to `true`
+
+**Note:** you can also pass in the map function instead of saving a design doc first, but this is slow because it has to do a full database scan. The following examples will use this pattern for simplicity's sake, but you should normally avoid it.
+
+#### Complex keys
+
+You can also use [complex keys](https://docs.couchdb.org/en/stable/ddocs/views/collation.html#complex-keys) for fancy ordering:
+
+{% include code/start.html id="query2" type="callback" %}
+{% highlight js %}
+function map(doc) {
+ // sort by last name, first name, and age
+ emit([doc.lastName, doc.firstName, doc.age]);
+}
+db.query(map, function (err, response) {
+ if (err) { return console.log(err); }
+ // handle result
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="query2" type="async" %}
+{% highlight js %}
+function map(doc) {
+ // sort by last name, first name, and age
+ emit([doc.lastName, doc.firstName, doc.age]);
+}
+try {
+ const result = await db.query(map);
+} catch (err) {
+ console.log(err);
+}
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="query2" type="promise" %}
+{% highlight js %}
+function map(doc) {
+ // sort by last name, first name, and age
+ emit([doc.lastName, doc.firstName, doc.age]);
+}
+db.query(map).then(function (result) {
+ // handle result
+}).catch(function (err) {
+ console.log(err);
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+#### Example Response:
+{% highlight js %}
+{
+ "offset": 0,
+ "rows": [{
+ "id" : "bowie",
+ "key" : ["Bowie", "David", 67]
+ }, {
+ "id" : "dylan",
+ "key" : ["Dylan", "Bob", 72]
+ }, {
+ "id" : "younger_dylan",
+ "key" : ["Dylan", "Jakob", 44]
+ }, {
+ "id" : "hank_the_third",
+ "key" : ["Williams", "Hank", 41]
+ }, {
+ "id" : "hank",
+ "key" : ["Williams", "Hank", 91]
+ }],
+ "total_rows": 5
+}
+{% endhighlight %}
+
+**Tips:**
+
+* The sort order is `[nulls, booleans, numbers, strings, arrays, objects]`, so `{startkey: ['Williams'], endkey: ['Williams', {}]}` would return all people with the last name `'Williams'` because objects are higher than strings. Something like `'zzzzz'` or `'\ufff0'` would also work.
+* `group_level` can be very helpful when working with complex keys. In the example above, you can use `{group_level: 1}` to group by last name, or `{group_level: 2}` to group by last and first name. (Be sure to set `{reduce: true, group: true}` as well.)
+
+#### Linked documents
+
+PouchDB fully supports [linked documents](https://docs.couchdb.org/en/stable/ddocs/views/joins.html?highlight=linked%20documents#linked-documents). Use them to join two types of documents together, by simply adding an `_id` to the emitted value:
+
+{% include code/start.html id="query3" type="callback" %}
+{% highlight js %}
+function map(doc) {
+ // join artist data to albums
+ if (doc.type === 'album') {
+ emit(doc.name, {_id : doc.artistId, albumYear : doc.year});
+ }
+}
+db.query(map, {include_docs : true}, function (err, response) {
+ if (err) { return console.log(err); }
+ // handle result
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="query3" type="async" %}
+{% highlight js %}
+function map(doc) {
+ // join artist data to albums
+ if (doc.type === 'album') {
+ emit(doc.name, {_id : doc.artistId, albumYear : doc.year});
+ }
+}
+try {
+ const result = await db.query(map, {include_docs : true});
+} catch (err) {
+ console.log(err);
+}
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="query3" type="promise" %}
+{% highlight js %}
+function map(doc) {
+ // join artist data to albums
+ if (doc.type === 'album') {
+ emit(doc.name, {_id : doc.artistId, albumYear : doc.year});
+ }
+}
+db.query(map, {include_docs : true}).then(function (result) {
+ // handle result
+}).catch(function (err) {
+ console.log(err);
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+#### Example response:
+
+{% highlight js %}
+{
+ "offset": 0,
+ "rows": [
+ {
+ "doc": {
+ "_id": "bowie",
+ "_rev": "1-fdb234b78904a5c8293f2acf4be70d44",
+ "age": 67,
+ "firstName": "David",
+ "lastName": "Bowie"
+ },
+ "id": "album_hunkeydory",
+ "key": "Hunky Dory",
+ "value": {
+ "_id": "bowie",
+ "albumYear": 1971
+ }
+ },
+ {
+ "doc": {
+ "_id": "bowie",
+ "_rev": "1-fdb234b78904a5c8293f2acf4be70d44",
+ "age": 67,
+ "firstName": "David",
+ "lastName": "Bowie"
+ },
+ "id": "album_low",
+ "key": "Low",
+ "value": {
+ "_id": "bowie",
+ "albumYear": 1977
+ }
+ },
+ {
+ "doc": {
+ "_id": "bowie",
+ "_rev": "1-fdb234b78904a5c8293f2acf4be70d44",
+ "age": 67,
+ "firstName": "David",
+ "lastName": "Bowie"
+ },
+ "id": "album_spaceoddity",
+ "key": "Space Oddity",
+ "value": {
+ "_id": "bowie",
+ "albumYear": 1969
+ }
+ }
+ ],
+ "total_rows": 3
+}
+{% endhighlight %}
+
+#### Closures
+
+If you pass a function to `db.query` and give it the `emit` function as the second argument, then you can use a closure. (Since PouchDB has to use `eval()` to bind `emit`.)
+
+{% include code/start.html id="query4" type="callback" %}
+{% highlight js %}
+// BAD! will throw error
+const myId = 'foo';
+db.query(function(doc) {
+ if (doc._id === myId) {
+ emit(doc);
+ }
+}, function(err, results) {
+ // you'll get an error here
+});
+
+// will be fine
+const myId = 'foo';
+db.query(function(doc, emit) {
+ if (doc._id === myId) {
+ emit(doc);
+ }
+}, function(err, results) {
+ if (err) { return console.log(err); }
+ // handle result
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="query4" type="async" %}
+{% highlight js %}
+// BAD! will throw error
+const myId = 'foo';
+try {
+ const result = await db.query(function(doc) {
+ if (doc._id === myId) {
+ emit(doc);
+ }
+ });
+} catch (err) {
+ // you'll get an error here
+}
+
+// will be fine
+const myId = 'foo';
+try {
+ const result = await db.query(function(doc, emit) {
+ if (doc._id === myId) {
+ emit(doc);
+ }
+ });
+} catch (err) {
+ console.log(err);
+}
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="query4" type="promise" %}
+{% highlight js %}
+// BAD! will throw error
+const myId = 'foo';
+db.query(function(doc) {
+ if (doc._id === myId) {
+ emit(doc);
+ }
+}).catch(function (err) {
+ // you'll get an error here
+}
+
+// will be fine
+const myId = 'foo';
+db.query(function(doc, emit) {
+ if (doc._id === myId) {
+ emit(doc);
+ }
+}).then(function (result) {
+ // handle result
+}).catch(function (err) {
+ console.log(err);
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+Note that closures are only supported by local databases with temporary views. So if you are using closures, then you must use the slower method that requires a full database scan.
diff --git a/docs/version/8.0.0/api/query_index.html b/docs/version/8.0.0/api/query_index.html
new file mode 100644
index 0000000000..7ea7ca8b8a
--- /dev/null
+++ b/docs/version/8.0.0/api/query_index.html
@@ -0,0 +1,338 @@
+{% include anchor.html edit="true" title="Query index" hash="query_index" %}
+
+{% highlight js %}
+db.find(request [, callback])
+{% endhighlight %}
+
+Query an index and return the list of documents that match the request.
+
+{% include alert/start.html variant="info"%}
+{% markdown %}
+
+**pouchdb-find plugin needed:** This API requires the `pouchdb-find` plugin. See
+[Mango queries](/guides/mango-queries.html) for installation instructions.
+
+{% endmarkdown %}
+{% include alert/end.html%}
+
+#### Example Usage:
+
+{% include code/start.html id="query_idx" type="callback" %}
+{% highlight js %}
+db.find({
+ selector: {name: 'Mario'},
+ fields: ['_id', 'name'],
+ sort: ['name']
+}, function (err, result) {
+ if (err) { return console.log(err); }
+ // handle result
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="query_idx" type="async" %}
+{% highlight js %}
+try {
+ const result = await db.find({
+ selector: {name: 'Mario'},
+ fields: ['_id', 'name'],
+ sort: ['name']
+ });
+} catch (err) {
+ console.log(err);
+}
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="query_idx" type="promise" %}
+{% highlight js %}
+db.find({
+ selector: {name: 'Mario'},
+ fields: ['_id', 'name'],
+ sort: ['name']
+}).then(function (result) {
+ // handle result
+}).catch(function (err) {
+ console.log(err);
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+#### Example Response:
+
+{% highlight js %}
+{
+ "docs": [
+ {
+ "_id": "mario",
+ "name": "Mario"
+ }
+ ]
+}
+{% endhighlight %}
+
+The above is a simple example. For an in-depth tutorial, please refer to
+[the Mango guide](/guides/mango-queries.html).
+
+### Options
+
+* `selector` Defines a selector to filter the results. Required.
+ * `$lt` Match fields "less than" this one.
+ * `$gt` Match fields "greater than" this one.
+ * `$lte` Match fields "less than or equal to" this one.
+ * `$gte` Match fields "greater than or equal to" this one.
+ * `$eq` Match fields equal to this one.
+ * `$ne` Match fields not equal to this one.
+ * `$exists` True if the field should exist, false otherwise.
+ * `$type` One of: "null", "boolean", "number", "string", "array", or "object".
+ * `$in` The document field must exist in the list provided.
+ * `$and` Matches if all the selectors in the array match.
+ * `$nin` The document field must not exist in the list provided.
+ * `$all` Matches an array value if it contains all the elements of the argument array.
+ * `$size` Special condition to match the length of an array field in a document.
+ * `$or` Matches if any of the selectors in the array match. All selectors must use the same index.
+ * `$nor` Matches if none of the selectors in the array match.
+ * `$not` Matches if the given selector does not match.
+ * `$mod` Matches documents where (field % Divisor == Remainder) is true, and only when the document field is an integer.
+ * `$regex` A regular expression pattern to match against the document field.
+ * `$elemMatch` Matches all documents that contain an array field with at least one element that matches all the specified query criteria.
+* `fields` (Optional) Defines a list of fields that you want to receive. If omitted, you get the full documents.
+* `sort` (Optional) Defines a list of fields defining how you want to sort. Note that sorted fields also have to be selected in the `selector`.
+* `limit` (Optional) Maximum number of documents to return. Default is `25` when connected to a local database.
+* `skip` (Optional) Number of docs to skip before returning.
+* `use_index` (Optional) Set which index to use for the query. It can be "design-doc-name" or "['design-doc-name', 'name']".
+
+If there's no index that matches your `selector`/`sort`, then this method will issue a warning:
+
+{% highlight js %}
+{
+ "docs": [ /* ... */ ],
+ "warning": "No matching index found, create an index to optimize query time."
+}
+{% endhighlight %}
+
+The best index will be chosen automatically.
+
+See the [CouchDB `_find` documentation](https://docs.couchdb.org/en/stable/api/database/find.html) for more details on
+selectors and the Mango query language.
+
+### More examples
+
+Use `$eq` for "equals":
+
+{% include code/start.html id="query_idx2" type="callback" %}
+{% highlight js %}
+db.find({
+ selector: {name: {$eq: 'Mario'}}
+}, function (err, result) {
+ if (err) { return console.log(err); }
+ // handle result
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="query_idx2" type="async" %}
+{% highlight js %}
+try {
+ const result = await db.find({
+ selector: {name: {$eq: 'Mario'}}
+ });
+} catch (err) {
+ console.log(err);
+}
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="query_idx2" type="promise" %}
+{% highlight js %}
+db.find({
+ selector: {name: {$eq: 'Mario'}}
+}).then(function (result) {
+ // handle result
+}).catch(function (err) {
+ console.log(err);
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+This is equivalent to:
+
+{% include code/start.html id="query_idx3" type="callback" %}
+{% highlight js %}
+db.find({
+ selector: {name: 'Mario'}
+}, function (err, result) {
+ if (err) { return console.log(err); }
+ // handle result
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="query_idx3" type="async" %}
+{% highlight js %}
+try {
+ const result = await db.find({
+ selector: {name: 'Mario'}
+ });
+} catch (err) {
+ console.log(err);
+}
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="query_idx3" type="promise" %}
+{% highlight js %}
+db.find({
+ selector: {name: 'Mario'}
+}).then(function (result) {
+ // handle result
+}).catch(function (err) {
+ console.log(err);
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+You can also do selections on multiple fields. For instance, to
+find all docs where `series` is `'Mario'` and `debut` is greater than `1990`:
+
+{% include code/start.html id="query_idx4" type="callback" %}
+{% highlight js %}
+db.find({
+ selector: {
+ series: 'Mario',
+ debut: { $gt: 1990 }
+ }
+}, function (err, result) {
+ if (err) { return console.log(err); }
+ // handle result
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="query_idx4" type="async" %}
+{% highlight js %}
+try {
+ const result = await db.find({
+ selector: {
+ series: 'Mario',
+ debut: { $gt: 1990 }
+ }
+ });
+} catch (err) {
+ console.log(err);
+}
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="query_idx4" type="promise" %}
+{% highlight js %}
+db.find({
+ selector: {
+ series: 'Mario',
+ debut: { $gt: 1990 }
+ }
+}).then(function (result) {
+ // handle result
+}).catch(function (err) {
+ console.log(err);
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+This is equivalent to:
+
+{% include code/start.html id="query_idx5" type="callback" %}
+{% highlight js %}
+db.find({
+ selector: {
+ $and: [
+ { series: 'Mario' },
+ { debut: { $gt: 1990 } }
+ ]
+ }
+}, function (err, result) {
+ if (err) { return console.log(err); }
+ // handle result
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="query_idx5" type="async" %}
+{% highlight js %}
+try {
+ const result = await db.find({
+ selector: {
+ $and: [
+ { series: 'Mario' },
+ { debut: { $gt: 1990 } }
+ ]
+ }
+ });
+} catch (err) {
+ console.log(err);
+}
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="query_idx5" type="promise" %}
+{% highlight js %}
+db.find({
+ selector: {
+ $and: [
+ { series: 'Mario' },
+ { debut: { $gt: 1990 } }
+ ]
+ }
+}).then(function (result) {
+ // handle result
+}).catch(function (err) {
+ console.log(err);
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+You can also sort the returned documents. For instance, to find all docs sorted by `debut` descending:
+
+{% include code/start.html id="query_idx6" type="callback" %}
+{% highlight js %}
+db.find({
+ selector: {
+ debut: {'$gte': null}
+ },
+ sort: [{debut: 'desc'}]
+}, function (err, result) {
+ if (err) { return console.log(err); }
+ // handle result
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="query_idx6" type="async" %}
+{% highlight js %}
+try {
+ const result = await db.find({
+ selector: {
+ debut: {'$gte': null}
+ },
+ sort: [{debut: 'desc'}]
+ });
+} catch (err) {
+ console.log(err);
+}
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="query_idx6" type="promise" %}
+{% highlight js %}
+db.find({
+ selector: {
+ debut: {'$gte': null}
+ },
+ sort: [{debut: 'desc'}]
+}).then(function (result) {
+ // handle result
+}).catch(function (err) {
+ console.log(err);
+});
+{% endhighlight %}
+{% include code/end.html %}
diff --git a/docs/version/8.0.0/api/replication.html b/docs/version/8.0.0/api/replication.html
new file mode 100644
index 0000000000..7ee4284e07
--- /dev/null
+++ b/docs/version/8.0.0/api/replication.html
@@ -0,0 +1,322 @@
+{% include anchor.html edit="true" title="Replicate a database" hash="replication" %}
+
+{% highlight js %}
+PouchDB.replicate(source, target, [options])
+{% endhighlight %}
+
+Replicate data from `source` to `target`. Both the `source` and `target` can be a PouchDB instance or a string representing a CouchDB database URL or the name of a local PouchDB database. If `options.live` is `true`, then this will track future changes and also replicate them automatically. This method returns an object with the method `cancel()`, which you call if you want to cancel live replication.
+
+Replication is an [event emitter][] like [changes()](#changes) and emits the `'complete'`, `'active'`, `'paused'`, `'change'`, `'denied'`, `'error'` and `'checkpoint'` events.
+
+### Options
+
+All options default to `false` unless otherwise specified.
+
+* `options.live`: If `true`, starts subscribing to future changes in the `source` database and continue replicating them.
+* `options.retry`: If `true` will attempt to retry replications in the case of failure (due to being offline), using a backoff algorithm that retries at longer and longer intervals until a connection is re-established, with a maximum delay of 10 minutes. Only applicable if `options.live` is also `true`.
+
+**Filtering Options:**
+
+* `options.filter`: Reference a filter function from a design document to selectively get updates. To use a view function, pass `_view` here and provide a reference to the view function in `options.view`. See [filtered replication](#filtered-replication) for details.
+* `options.doc_ids`: Only show changes for docs with these ids (array of strings).
+* `options.query_params`: Object containing properties that are passed to the filter function, e.g. `{"foo":"bar"}`, where `"bar"` will be available in the filter function as `params.query.foo`. To access the `params`, define your filter function like `function (doc, params) {/* ... */}`.
+* `options.view`: Specify a view function (e.g. `'design_doc_name/view_name'` or `'view_name'` as shorthand for `'view_name/view_name'`) to act as a filter. Documents counted as "passed" for a view filter if a map function emits at least one record for them. **Note**: `options.filter` must be set to `'_view'` for this option to work.
+* `options.selector`: Filter using a query/pouchdb-find [selector](https://docs.couchdb.org/en/stable/api/database/find.html#find-selectors).
+
+**Advanced Options:**
+
+* `options.since`: Replicate changes after the given sequence number.
+* `options.heartbeat`: Configure the heartbeat supported by CouchDB which keeps the change connection alive.
+* `options.timeout`: Request timeout (in milliseconds).
+* `options.batch_size`: Number of change feed items to process at a time. Defaults to 100. This affects the number of docs and attachments held in memory and the number sent at a time to the target server. You may need to adjust downward if targeting devices with low amounts of memory (e.g. phones) or if the documents and/or attachments are large in size or if there are many conflicted revisions. If your documents are small in size, then increasing this number will probably speed replication up.
+* `options.batches_limit`: Number of batches to process at a time. Defaults to 10. This (along with `batch_size`) controls how many docs are kept in memory at a time, so the maximum docs in memory at once would equal `batch_size` × `batches_limit`.
+* `options.back_off_function`: backoff function to be used in `retry` replication. This is a function that takes the current backoff as input (or 0 the first time) and returns a new backoff in milliseconds. You can use this to tweak when and how replication will try to reconnect to a remote database when the user goes offline. Defaults to a function that chooses a random backoff between 0 and 2 seconds and doubles every time it fails to connect. The default delay will never exceed 10 minutes. (See [Customizing retry replication](#customizing-retry-replication) below.)
+* `options.checkpoint`: Can be used if you want to disable checkpoints on the source, target, or both. Setting this option to `false` will prevent writing checkpoints on both source and target. Setting it to `source` will only write checkpoints on the source. Setting it to `target` will only write checkpoints on the target.
+* `options.style`: Specifies whether all revisions of a document including conflicts and deleted former conflicts (`all_docs`) or only the winning revision (`main_only`) should be replicated. This option is passed to the `changes` endpoint of the replication source. Defaults to `all_docs`.
+
+#### Example Usage:
+
+{% highlight js %}
+const rep = PouchDB.replicate('mydb', 'http://localhost:5984/mydb', {
+ live: true,
+ retry: true
+}).on('change', function (info) {
+ // handle change
+}).on('paused', function (err) {
+ // replication paused (e.g. replication up to date, user went offline)
+}).on('active', function () {
+ // replicate resumed (e.g. new changes replicating, user went back online)
+}).on('denied', function (err) {
+ // a document failed to replicate (e.g. due to permissions)
+}).on('complete', function (info) {
+ // handle complete
+}).on('error', function (err) {
+ // handle error
+});
+
+rep.cancel(); // whenever you want to cancel
+{% endhighlight %}
+
+There are also shorthands for replication given existing PouchDB objects. These behave the same as `PouchDB.replicate()`:
+
+{% highlight js %}
+db.replicate.to(remoteDB, [options]);
+// or
+db.replicate.from(remoteDB, [options]);
+{% endhighlight %}
+
+The `remoteDB` can either be a string or a `PouchDB` object. If you have a fetch override on a remote database, you will want to use `PouchDB` objects instead of strings, so that the options are used.
+
+#### Replication events
+
+* __`change`__ (`info`) - This event fires when the replication has written a new document. `info` will contain details about the change. `info.docs` will contain the docs involved in that change. See below for an example response.
+* __`complete`__ (`info`) - This event fires when replication is completed or cancelled. In a live replication, only cancelling the replication should trigger this event. `info` will contain details about the replication. See below for an example response.
+* __`paused`__ (`err`) - This event fires when the replication is paused, either because a live replication is waiting for changes, or replication has temporarily failed, with `err`, and is attempting to resume.
+* __`active`__ - This event fires when the replication starts actively processing changes; e.g. when it recovers from an error or new changes are available.
+* __`denied`__ (`err`) - This event fires if a document failed to replicate due to validation or authorization errors.
+* __`error`__ (`err`) - This event is fired when the replication is stopped due to an unrecoverable failure. If `retry` is `false`, this will also fire when the user goes offline or another network error occurs (so you can handle retries yourself, if you want).
+* __`checkpoint`__ - This event exposes information about the internal steps of the replication process. It includes one of the fields `pending_batch`, `start_next_batch`, `revs_diff` or `checkpoint`.
+
+#### Single-shot
+
+As with [changes()](#changes), you can also omit `live`, in which case you can use `replicate()` in the callback/promise style and it will be treated as a single-shot operation.
+
+{% include code/start.html id="replication1" type="callback" %}
+{% highlight js %}
+db.replicate.to(remote, function (err, result) {
+ if (err) { return console.log(err); }
+ // handle 'completed' result
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="replication1" type="async" %}
+{% highlight js %}
+try {
+ const result = await db.replicate.to(remote);
+} catch (err) {
+ console.log(err);
+}
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="replication1" type="promise" %}
+{% highlight js %}
+db.replicate.to(remote).then(function (result) {
+ // handle 'completed' result
+}).catch(function (err) {
+ console.log(err);
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+For non-live replications, the returned object is also an event emitter as well as a promise, and you can use all the events described above (except for `'paused'` and `'active'`, which only apply to `retry` replications).
+
+#### Example Response:
+
+Example response in the `'change'` listener:
+
+{% highlight js %}
+{
+ "doc_write_failures": 0,
+ "docs_read": 1,
+ "docs_written": 1,
+ "errors": [],
+ "last_seq": 1,
+ "ok": true,
+ "start_time": "Fri May 16 2014 18:23:12 GMT-0700 (PDT)",
+ "docs": [
+ { _id: 'docId',
+ _rev: '1-e798a1a7eb4247b399dbfec84ca699d4',
+ and: 'data' }
+ ]
+}
+{% endhighlight %}
+
+Example response in the `'complete'` listener:
+
+{% highlight js %}
+{
+ "doc_write_failures": 0,
+ "docs_read": 2,
+ "docs_written": 2,
+ "end_time": "Fri May 16 2014 18:26:00 GMT-0700 (PDT)",
+ "errors": [],
+ "last_seq": 2,
+ "ok": true,
+ "start_time": "Fri May 16 2014 18:26:00 GMT-0700 (PDT)",
+ "status": "complete"
+}
+{% endhighlight %}
+
+Note that replication is supported for both local and remote databases. So you can replicate from local to local or from remote to remote.
+
+However, if you replicate from remote to remote, then the changes will flow through PouchDB. If you want to trigger a server-initiated replication, please use regular ajax to POST to the CouchDB `_replicate` endpoint, as described [in the CouchDB docs](https://docs.couchdb.org/en/stable/api/server/common.html#api-server-replicate).
+
+#### Filtered replication
+
+As with [changes()](#changes), you can filter from the source database using:
+
+* an ad-hoc `filter` function
+* an array of `doc_ids`
+* a `filter` function inside of a design document
+* a `filter` function inside of a design document, with `query_params`
+* a `view` function inside of a design document
+
+If you are replicating from a remote CouchDB, then the first method will run client-side, whereas the last four will filter on the server side. Therefore the last four should be preferred, especially if the database is large, because you want to send as few documents over the wire as possible.
+
+You should also beware trying to use filtered replication to enforce security, e.g. to partition a database per user. A better strategy is the ["one database per user" method](https://github.com/nolanlawson/pouchdb-authentication#couchdb-authentication-recipes).
+
+{% include alert/start.html variant="warning" %}
+
+{% markdown %}
+
+**Deleting filtered docs**: When you use filtered replication, you should avoid using `remove()` to delete documents, because that removes all their fields as well, which means they might not pass the filter function anymore, causing the deleted revision to not be replicated. Instead, set the `doc._deleted` flag to `true` and then use `put()` or `bulkDocs()`.
+
+{% endmarkdown %}
+
+{% include alert/end.html %}
+
+#### Filtering examples
+
+In these examples, we'll work with some mammals. Let's imagine our docs are:
+
+{% highlight js %}
+[
+ {_id: 'a', name: 'Kangaroo', type: 'marsupial'},
+ {_id: 'b', name: 'Koala', type: 'marsupial'},
+ {_id: 'c', name: 'Platypus', type: 'monotreme'}
+]
+{% endhighlight %}
+
+Here are 5 examples using the 5 different systems.
+
+**Example 1: Ad-hoc `filter` function**
+
+*Warning*: this runs client-side, if you are replicating from a remote database.
+
+Filter by `type === 'marsupial'`:
+
+{% highlight js %}
+remote.replicate.to(local, {
+ filter: function (doc) {
+ return doc.type === 'marsupial';
+ }
+});
+{% endhighlight %}
+
+**Example 2: Array of `doc_ids`**
+
+Filter documents with `_id`s `['a', 'c']`.
+
+{% highlight js %}
+remote.replicate.to(local, {
+ doc_ids: ['a', 'c']
+});
+{% endhighlight %}
+
+**Example 3: `filter` function inside of a design document**
+
+First `put()` a design document in the remote database:
+
+{% highlight js %}
+{
+ _id: '_design/mydesign',
+ filters: {
+ myfilter: function (doc) {
+ return doc.type === 'marsupial';
+ }.toString()
+ }
+}
+{% endhighlight %}
+
+Then filter by `type === 'marsupial'`:
+
+{% highlight js %}
+remote.replicate.to(local, {
+ filter: 'mydesign/myfilter'
+});
+{% endhighlight %}
+
+**Example 4: `filter` function inside of a design document, with `query_params`**
+
+This is the most powerful way to filter, because it allows you to pass in arbitrary options to your filter function.
+
+First `put()` a design document in the remote database:
+
+{% highlight js %}
+{
+ _id: '_design/mydesign',
+ filters: {
+ myfilter: function (doc, req) {
+ return doc.type === req.query.type;
+ }.toString()
+ }
+}
+{% endhighlight %}
+
+Then filter by `type === 'marsupial'`:
+
+{% highlight js %}
+remote.replicate.to(local, {
+ filter: 'mydesign/myfilter',
+ query_params: {type: 'marsupial'}
+});
+{% endhighlight %}
+
+**Example 5: `view` function inside of a design document**
+
+This doesn't really offer any advantages compared to the previous two methods, unless you are already using a `view` for map/reduce queries, and you want to reuse it.
+
+Any documents that `emit()` anything will be considered to have passed this filter method.
+
+First `put()` a design document in the remote database:
+
+{% highlight js %}
+{
+ _id: '_design/mydesign',
+ views: {
+ myview: {
+ map: function(doc) {
+ if (doc.type === 'marsupial') {
+ emit(doc._id);
+ }
+ }.toString()
+ }
+ }
+}
+{% endhighlight %}
+
+Then filter by `type === 'marsupial'`:
+
+{% highlight js %}
+remote.replicate.to(local, {
+ filter: '_view',
+ view: 'mydesign/myview'
+});
+{% endhighlight %}
+
+#### Customizing retry replication
+
+During `retry` replication, you can customize the backoff function that determines how long to wait before reconnecting when the user goes offline.
+
+Here's a simple backoff function that starts at 1000 milliseconds and triples it every time a remote request fails:
+
+{% highlight js %}
+
+db.replicate.to(remote, {
+ live: true,
+ retry: true,
+ back_off_function: function (delay) {
+ if (delay === 0) {
+ return 1000;
+ }
+ return delay * 3;
+ }
+});
+
+{% endhighlight %}
+
+The first time a request fails, this function will receive 0 as input. The next time it fails, 1000 will be passed in, then 3000, then 9000, etc. When the user comes back online, the `delay` goes back to 0.
+
+By default, PouchDB uses a backoff function that chooses a random starting number between 0 and 2000 milliseconds and will roughly double every time, with some randomness to prevent client requests from occurring simultaneously.
diff --git a/docs/version/8.0.0/api/revisions_diff.html b/docs/version/8.0.0/api/revisions_diff.html
new file mode 100644
index 0000000000..9bd6c45607
--- /dev/null
+++ b/docs/version/8.0.0/api/revisions_diff.html
@@ -0,0 +1,65 @@
+{% include anchor.html edit="true" title="Document revisions diff" hash="revisions_diff" %}
+
+{% highlight js %}
+db.revsDiff(diff, [callback])
+{% endhighlight %}
+
+Given a set of document/revision IDs, returns the subset of those that do not correspond
+to revisions stored in the database. Primarily used in replication.
+
+#### Example Usage:
+
+{% include code/start.html id="revsdiff1" type="callback" %}
+{% highlight js %}
+db.revsDiff({
+ myDoc1: [
+ "1-b2e54331db828310f3c772d6e042ac9c",
+ "2-3a24009a9525bde9e4bfa8a99046b00d"
+ ]
+}, function (err, result) {
+ if (err) { return console.log(err); }
+ // handle result
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="revsdiff1" type="async" %}
+{% highlight js %}
+try {
+ const result = await db.revsDiff({
+ myDoc1: [
+ "1-b2e54331db828310f3c772d6e042ac9c",
+ "2-3a24009a9525bde9e4bfa8a99046b00d"
+ ]
+ });
+} catch (err) {
+ console.log(err);
+}
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="revsdiff1" type="promise" %}
+{% highlight js %}
+db.revsDiff({
+ myDoc1: [
+ "1-b2e54331db828310f3c772d6e042ac9c",
+ "2-3a24009a9525bde9e4bfa8a99046b00d"
+ ]
+}).then(function (result) {
+ // handle result
+}).catch(function (err) {
+ console.log(err);
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+#### Example Response:
+{% highlight js %}
+{
+ "myDoc1": {
+ "missing": ["2-3a24009a9525bde9e4bfa8a99046b00d"]
+ }
+}
+{% endhighlight %}
+
+
diff --git a/docs/version/8.0.0/api/save_attachment.html b/docs/version/8.0.0/api/save_attachment.html
new file mode 100644
index 0000000000..3f2ac38207
--- /dev/null
+++ b/docs/version/8.0.0/api/save_attachment.html
@@ -0,0 +1,349 @@
+{% include anchor.html edit="true" title="Save an attachment" hash="save_attachment" %}
+
+{% highlight js %}
+db.putAttachment(docId, attachmentId, [rev], attachment, type, [callback]);
+{% endhighlight %}
+
+Attaches a binary object to a document.
+
+This method will update an existing document to add the attachment, so it requires a `rev` if the document already exists. If the document doesn't already exist, then this method will create an empty document containing the attachment.
+
+What's the point of attachments? If you're dealing with large binary data (such as PNGs), you may incur a performance or storage penalty if you naïvely include them as base64- or hex-encoded strings inside your documents. But if you insert the binary data as an attachment, then PouchDB will attempt to store it in [the most efficient way possible]({{ site.baseurl }}/faq.html#data_types).
+
+For details, see the [CouchDB documentation on attachments](https://docs.couchdb.org/en/stable/api/document/attachments.html#put--db-docid-attname).
+
+#### Example Usage:
+
+{% include code/start.html id="attach1" type="callback" %}
+{% highlight js %}
+const attachment = new Blob(['Is there life on Mars?'], {type: 'text/plain'});
+db.putAttachment('doc', 'att.txt', attachment, 'text/plain', function(err, res) {
+ if (err) { return console.log(err); }
+ // handle result
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="attach1" type="async" %}
+{% highlight js %}
+try {
+ const attachment = new Blob(['Is there life on Mars?'], {type: 'text/plain'});
+ const result = await db.putAttachment('doc', 'att.txt', attachment, 'text/plain');
+} catch (err) {
+ console.log(err);
+}
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="attach1" type="promise" %}
+{% highlight js %}
+const attachment = new Blob(['Is there life on Mars?'], {type: 'text/plain'});
+db.putAttachment('doc', 'att.txt', attachment, 'text/plain').then(function (result) {
+ // handle result
+}).catch(function (err) {
+ console.log(err);
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+
+#### Example Response:
+{% highlight js %}
+{
+ "ok": true,
+ "id": "doc",
+ "rev": "2-068E73F5B44FEC987B51354DFC772891"
+}
+{% endhighlight %}
+
+Within Node, you must use a `Buffer` instead of a `Blob`:
+
+{% highlight js %}
+const attachment = new Buffer('Is there life on Mars?');
+{% endhighlight %}
+
+For details, see the [Mozilla docs on `Blob`](https://developer.mozilla.org/en-US/docs/Web/API/Blob) or the [Node docs on `Buffer`](http://nodejs.org/api/buffer.html).
+
+If you need a shim for older browsers that don't support the `Blob` constructor, or you want some convenience methods for Blobs, you can use [blob-util](https://github.com/nolanlawson/blob-util).
+
+#### Save a base64 attachment
+
+If you supply a string instead of a `Blob`/`Buffer`, then it will be assumed to be a base64-encoded string, and will be processed accordingly:
+
+{% include code/start.html id="attach3" type="callback" %}
+{% highlight js %}
+const attachment =
+ "TGVnZW5kYXJ5IGhlYXJ0cywgdGVhciB1cyBhbGwgYXBhcnQKTWFrZS" +
+ "BvdXIgZW1vdGlvbnMgYmxlZWQsIGNyeWluZyBvdXQgaW4gbmVlZA==";
+db.putAttachment('doc', 'att.txt', attachment, 'text/plain', function(err, res) {
+ if (err) { return console.log(err); }
+ // handle result
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="attach3" type="async" %}
+{% highlight js %}
+try {
+ const attachment =
+ "TGVnZW5kYXJ5IGhlYXJ0cywgdGVhciB1cyBhbGwgYXBhcnQKTWFrZS" +
+ "BvdXIgZW1vdGlvbnMgYmxlZWQsIGNyeWluZyBvdXQgaW4gbmVlZA==";
+ const result = await db.putAttachment('doc', 'att.txt', attachment, 'text/plain');
+} catch (err) {
+ console.log(err);
+}
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="attach3" type="promise" %}
+{% highlight js %}
+const attachment =
+ "TGVnZW5kYXJ5IGhlYXJ0cywgdGVhciB1cyBhbGwgYXBhcnQKTWFrZS" +
+ "BvdXIgZW1vdGlvbnMgYmxlZWQsIGNyeWluZyBvdXQgaW4gbmVlZA==";
+db.putAttachment('doc', 'att.txt', attachment, 'text/plain').then(function (result) {
+ // handle result
+}).catch(function (err) {
+ console.log(err);
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+
+#### Save an inline attachment
+
+You can also inline attachments inside the document. The attachment data may be supplied as a base64-encoded string with the `content_type`:
+
+{% include code/start.html id="attach2" type="callback" %}
+{% highlight js %}
+const doc = {
+ "_id": "doc",
+ "title": "Legendary Hearts",
+ "_attachments": {
+ "att.txt": {
+ "content_type": "text/plain",
+ "data": "TGVnZW5kYXJ5IGhlYXJ0cywgdGVhciB1cyBhbGwgYXBhcnQKTWFrZS" +
+ "BvdXIgZW1vdGlvbnMgYmxlZWQsIGNyeWluZyBvdXQgaW4gbmVlZA=="
+ }
+ }
+};
+db.put(doc, function (err, result) {
+ if (err) { return console.log(err); }
+ // handle result
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="attach2" type="async" %}
+{% highlight js %}
+try {
+ const doc = {
+ "_id": "doc",
+ "title": "Legendary Hearts",
+ "_attachments": {
+ "att.txt": {
+ "content_type": "text/plain",
+ "data": "TGVnZW5kYXJ5IGhlYXJ0cywgdGVhciB1cyBhbGwgYXBhcnQKTWFrZS" +
+ "BvdXIgZW1vdGlvbnMgYmxlZWQsIGNyeWluZyBvdXQgaW4gbmVlZA=="
+ }
+ }
+ };
+ const result = await db.put(doc);
+} catch (err) {
+ console.log(err);
+}
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="attach2" type="promise" %}
+{% highlight js %}
+const doc = {
+ "_id": "doc",
+ "title": "Legendary Hearts",
+ "_attachments": {
+ "att.txt": {
+ "content_type": "text/plain",
+ "data": "TGVnZW5kYXJ5IGhlYXJ0cywgdGVhciB1cyBhbGwgYXBhcnQKTWFrZS" +
+ "BvdXIgZW1vdGlvbnMgYmxlZWQsIGNyeWluZyBvdXQgaW4gbmVlZA=="
+ }
+ }
+};
+db.put(doc).then(function (result) {
+ // handle result
+}).catch(function (err) {
+ console.log(err);
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+#### Save an inline Blob/Buffer attachment
+
+You can also inline `Blob`s/`Buffer`s:
+
+{% include code/start.html id="attach4" type="callback" %}
+{% highlight js %}
+const doc = {
+ "_id": "doc",
+ "title": "Legendary Hearts",
+ "_attachments": {
+ "att.txt": {
+ "content_type": "text/plain",
+ "data": new Blob(['Is there life on Mars?'], {type: 'text/plain'})
+ }
+ }
+};
+db.put(doc, function (err, result) {
+ if (err) { return console.log(err); }
+ // handle result
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="attach4" type="async" %}
+{% highlight js %}
+try {
+ const doc = {
+ "_id": "doc",
+ "title": "Legendary Hearts",
+ "_attachments": {
+ "att.txt": {
+ "content_type": "text/plain",
+ "data": new Blob(['Is there life on Mars?'], {type: 'text/plain'})
+ }
+ }
+ };
+ const result = await db.put(doc);
+} catch (err) {
+ console.log(err);
+}
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="attach4" type="promise" %}
+{% highlight js %}
+const doc = {
+ "_id": "doc",
+ "title": "Legendary Hearts",
+ "_attachments": {
+ "att.txt": {
+ "content_type": "text/plain",
+ "data": new Blob(['Is there life on Mars?'], {type: 'text/plain'})
+ }
+ }
+};
+db.put(doc).then(function (result) {
+ // handle result
+}).catch(function (err) {
+ console.log(err);
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+#### Save many attachments at once
+
+The inline approach allows you to save multiple attachments to the same document in a single shot:
+
+{% include code/start.html id="attach5" type="callback" %}
+{% highlight js %}
+const doc = {
+ "_id": "doc",
+ "title": "Legendary Hearts",
+ "_attachments": {
+ "att.txt": {
+ "content_type": "text/plain",
+ "data": new Blob(
+ ["And she's hooked to the silver screen"],
+ {type: 'text/plain'})
+ },
+ "att2.txt": {
+ "content_type": "text/plain",
+ "data": new Blob(
+ ["But the film is a saddening bore"],
+ {type: 'text/plain'})
+ },
+ "att3.txt": {
+ "content_type": "text/plain",
+ "data": new Blob(
+ ["For she's lived it ten times or more"],
+ {type: 'text/plain'})
+ }
+ }
+};
+db.put(doc, function (err, result) {
+ if (err) { return console.log(err); }
+ // handle result
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="attach5" type="async" %}
+{% highlight js %}
+try {
+ const doc = {
+ "_id": "doc",
+ "title": "Legendary Hearts",
+ "_attachments": {
+ "att.txt": {
+ "content_type": "text/plain",
+ "data": new Blob(
+ ["And she's hooked to the silver screen"],
+ {type: 'text/plain'})
+ },
+ "att2.txt": {
+ "content_type": "text/plain",
+ "data": new Blob(
+ ["But the film is a saddening bore"],
+ {type: 'text/plain'})
+ },
+ "att3.txt": {
+ "content_type": "text/plain",
+ "data": new Blob(
+ ["For she's lived it ten times or more"],
+ {type: 'text/plain'})
+ }
+ }
+ };
+ const result = await db.put(doc);
+} catch (err) {
+ console.log(err);
+}
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="attach5" type="promise" %}
+{% highlight js %}
+const doc = {
+ "_id": "doc",
+ "title": "Legendary Hearts",
+ "_attachments": {
+ "att.txt": {
+ "content_type": "text/plain",
+ "data": new Blob(
+ ["And she's hooked to the silver screen"],
+ {type: 'text/plain'})
+ },
+ "att2.txt": {
+ "content_type": "text/plain",
+ "data": new Blob(
+ ["But the film is a saddening bore"],
+ {type: 'text/plain'})
+ },
+ "att3.txt": {
+ "content_type": "text/plain",
+ "data": new Blob(
+ ["For she's lived it ten times or more"],
+ {type: 'text/plain'})
+ }
+ }
+};
+db.put(doc).then(function (result) {
+ // handle result
+}).catch(function (err) {
+ console.log(err);
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+See [Inline Attachments](https://docs.couchdb.org/en/stable/api/document/common.html?highlight=inline%20attachment#creating-multiple-attachments)
+on the CouchDB wiki for details.
+
+
diff --git a/docs/version/8.0.0/api/sync.html b/docs/version/8.0.0/api/sync.html
new file mode 100644
index 0000000000..eb75ed15b7
--- /dev/null
+++ b/docs/version/8.0.0/api/sync.html
@@ -0,0 +1,96 @@
+{% include anchor.html edit="true" title="Sync a database" hash="sync" %}
+
+{% highlight js %}
+const sync = PouchDB.sync(src, target, [options])
+{% endhighlight %}
+
+Sync data from `src` to `target` and `target` to `src`. This is a convenience method for bidirectional data replication.
+
+In other words, this code:
+
+{% highlight js %}
+PouchDB.replicate('mydb', 'http://localhost:5984/mydb');
+PouchDB.replicate('http://localhost:5984/mydb', 'mydb');
+{% endhighlight %}
+
+
+is equivalent to this code:
+
+{% highlight js %}
+PouchDB.sync('mydb', 'http://localhost:5984/mydb');
+{% endhighlight %}
+
+
+### Options
+
+* `options.push` + `options.pull`: Allows you to specify separate [replication options](api.html#replication) for the individual replications.
+
+Replication options such as `filter` passed to sync directly will be passed to both replications. Please refer to [replicate()](api.html#replication) for documentation on those options.
+
+#### Example Usage:
+{% highlight js %}
+const sync = PouchDB.sync('mydb', 'http://localhost:5984/mydb', {
+ live: true,
+ retry: true
+}).on('change', function (info) {
+ // handle change
+}).on('paused', function (err) {
+ // replication paused (e.g. replication up to date, user went offline)
+}).on('active', function () {
+ // replicate resumed (e.g. new changes replicating, user went back online)
+}).on('denied', function (err) {
+ // a document failed to replicate (e.g. due to permissions)
+}).on('complete', function (info) {
+ // handle complete
+}).on('error', function (err) {
+ // handle error
+});
+
+sync.cancel(); // whenever you want to cancel
+{% endhighlight %}
+
+There is also a shorthand for syncing given existing PouchDB objects. This behaves the same as `PouchDB.sync()`:
+
+{% highlight js %}
+db.sync(remoteDB, [options]);
+{% endhighlight %}
+
+It is also possible to combine "one-way" replication and sync for performance reasons.
+When your PouchDB application starts up it could perform a one-off, one-way replication to completion and then initiate the two-way, continuous retryable sync:
+
+{% highlight js %}
+const url = 'http://localhost:5984/mydb';
+const opts = { live: true, retry: true };
+
+// do one way, one-off sync from the server until completion
+db.replicate.from(url).on('complete', function(info) {
+ // then two-way, continuous, retriable sync
+ db.sync(url, opts)
+ .on('change', onSyncChange)
+ .on('paused', onSyncPaused)
+ .on('error', onSyncError);
+}).on('error', onSyncError);
+{% endhighlight %}
+
+The above technique results in fewer HTTP requests being used and better performance than just using `db.sync` on its own.
+
+#### Example Response:
+
+Change events in `sync` have an extra property `direction` which refers to the direction the change was going. Its value will either be `push` or `pull`.
+
+{% highlight js %}
+{ direction: 'push',
+ change:
+ { ok: true,
+ start_time: '2015-10-21T15:26:51.151Z',
+ docs_read: 1,
+ docs_written: 1,
+ doc_write_failures: 0,
+ errors: [],
+ last_seq: 1,
+ docs: [ [Object] ] } }
+{% endhighlight %}
+
+For any further details, please refer to [replicate()](api.html#replication).
+
+
diff --git a/docs/version/8.0.0/api/view_cleanup.html b/docs/version/8.0.0/api/view_cleanup.html
new file mode 100644
index 0000000000..4df285312d
--- /dev/null
+++ b/docs/version/8.0.0/api/view_cleanup.html
@@ -0,0 +1,47 @@
+{% include anchor.html edit="true" title="View cleanup" hash="view_cleanup" %}
+
+{% highlight js %}
+db.viewCleanup([callback])
+{% endhighlight %}
+
+Cleans up any stale map/reduce indexes.
+
+As design docs are deleted or modified, their associated index files (in CouchDB) or companion databases (in local PouchDBs) continue to take up space on disk. `viewCleanup()` removes these unnecessary index files.
+
+See [the CouchDB documentation on view cleanup](http://couchdb.readthedocs.org/en/latest/maintenance/compaction.html#views-cleanup) for details.
+
+#### Example Usage:
+
+{% include code/start.html id="viewcleanup" type="callback" %}
+{% highlight js %}
+db.viewCleanup(function (err, result) {
+ if (err) { return console.log(err); }
+ // handle result
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="viewcleanup" type="async" %}
+{% highlight js %}
+try {
+ const result = await db.viewCleanup();
+} catch (err) {
+ console.log(err);
+}
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="viewcleanup" type="promise" %}
+{% highlight js %}
+db.viewCleanup().then(function (result) {
+ // handle result
+}).catch(function (err) {
+ console.log(err);
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+#### Example Response:
+{% highlight js %}
+{ "ok" : "true" }
+{% endhighlight %}
\ No newline at end of file
diff --git a/docs/version/8.0.0/custom.md b/docs/version/8.0.0/custom.md
new file mode 100644
index 0000000000..b8095cc4aa
--- /dev/null
+++ b/docs/version/8.0.0/custom.md
@@ -0,0 +1,539 @@
+---
+layout: 2ColLeft.html
+title: Custom Builds
+sidebar: ./nav.html
+---
+
+PouchDB supports custom builds, meaning you can pick and choose the features of
+PouchDB that you want to use, potentially resulting in smaller bundle sizes
+and faster build times.
+
+PouchDB exposes its custom builds via separate packages available on npm. All of
+these packages follow the format `pouchdb-` and can be installed using `npm install`.
+
+Some packages are included by default in the main `pouchdb` package, whereas
+others (including third-party packages) must be installed separately.
+
+{% include alert/start.html variant="warning"%}
+{% markdown %}
+
+Custom builds require an [npm][]-based build system, using a bundler
+like [Browserify][], [Webpack][], [SystemJS][], [Rollup][], or [JSPM][]. Tools like
+[Bower][], as well as direct download of prebuilt JavaScript files, are not supported.
+
+[Browserify]: http://browserify.org/
+[Webpack]: http://webpack.github.io/
+[SystemJS]: https://github.com/systemjs/systemjs
+[JSPM]: http://jspm.io/
+[Rollup]: http://rollupjs.org/
+[npm]: http://npmjs.com/
+[Bower]: http://bower.io
+
+{% endmarkdown %}
+{% include alert/end.html%}
+
+PouchDB packages come in three flavors: *presets*, *plugins*, and
+*utilities*.
+
+**Presets** are a collection of plugins, which expose a `PouchDB` object that is ready to be used.
+
+**Plugins** are features that can be added to a `PouchDB` instance using the `PouchDB.plugin()`
+API.
+
+**Utilities** are grab-bags of helper functions, and are only recommended for advanced use cases.
+
+#### Quick links
+
+* [Presets](#presets)
+* [Plugins](#plugins)
+* [Utilities](#utilities)
+
+{% include anchor.html class="h2" title="Presets" hash="presets" %}
+
+Presets export a `PouchDB` object and contain a built-in set of PouchDB
+plugins. You are free to create your own presets, but PouchDB provides a few first-party presets to address common use cases.
+
+### [pouchdb-browser](https://npmjs.org/package/pouchdb-browser)
+
+The `pouchdb-browser` preset contains the version of PouchDB that is designed
+for the browser. In particular, it ships with the IndexedDB adapter
+as its default adapter. It also contains the replication, HTTP, and map/reduce plugins.
+
+Use this preset if you only want to use PouchDB in the browser,
+and don't want to use it in Node.js. (E.g. to avoid installing LevelDB.)
+
+#### Example Usage
+
+```bash
+npm install pouchdb-browser
+```
+
+```js
+const PouchDB = require('pouchdb-browser');
+const db = new PouchDB('mydb');
+```
+
+#### Source code (simplified)
+
+```js
+const PouchDB = require('pouchdb-core')
+ .plugin(require('pouchdb-adapter-idb'))
+ .plugin(require('pouchdb-adapter-http'))
+ .plugin(require('pouchdb-mapreduce'))
+ .plugin(require('pouchdb-replication'));
+```
+
+### [pouchdb-node](https://npmjs.org/package/pouchdb-node)
+
+The `pouchdb-node` preset contains the version of PouchDB that is designed for
+Node.js. In particular, it uses the LevelDB adapter and doesn't ship with the
+IndexedDB or WebSQL adapters. It also contains the replication, HTTP, and map/reduce plugins.
+
+Use this preset if you are only using PouchDB in Node, and not in the browser.
+
+#### Example Usage
+
+```bash
+npm install pouchdb-node
+```
+
+```js
+const PouchDB = require('pouchdb-node');
+const db = new PouchDB('mydb');
+```
+
+#### Source code (simplified)
+
+```js
+const PouchDB = require('pouchdb-core')
+ .plugin(require('pouchdb-adapter-leveldb'))
+ .plugin(require('pouchdb-adapter-http'))
+ .plugin(require('pouchdb-mapreduce'))
+ .plugin(require('pouchdb-replication'));
+```
+
+### [pouchdb-core](https://npmjs.org/package/pouchdb-core)
+
+The `pouchdb-core` package is a special preset in that it exposes the minimum
+number of APIs. It contains zero plugins and is designed to be used in addition
+with other plugins. By itself, it probably isn't very useful.
+
+#### Example Usage
+
+```bash
+npm install pouchdb-core
+```
+
+```js
+const PouchDB = require('pouchdb-core');
+PouchDB.plugin(/* attach plugins to make me more interesting! */);
+```
+
+{% include anchor.html class="h2" title="Plugins" hash="plugins" %}
+
+Plugins contain functionality that can be added to a `PouchDB` instance using `PouchDB.plugin()`. There are many [third-party plugins](/external.html), but the ones described below are first-party plugins, which are given the same level of support as PouchDB itself. Some first-party plugins are included in the default `pouchdb` build, whereas others aren't.
+
+There is also a special type of plugin called an _adapter plugin_. Adapter plugins (such as IndexedDB, WebSQL, LevelDB, and HTTP) determine the storage format that
+PouchDB uses. For the non-HTTP adapters, the plugin order matters, i.e. if you
+want IndexedDB to be preferred to WebSQL, then you should load it first.
+
+### [pouchdb-adapter-idb](https://npmjs.org/package/pouchdb-adapter-idb)
+
+The primary adapter used by PouchDB in the browser, using IndexedDB. The adapter
+name is `'idb'`.
+
+#### Example usage
+
+```bash
+npm install pouchdb-adapter-idb
+```
+
+```js
+PouchDB.plugin(require('pouchdb-adapter-idb'));
+const db = new PouchDB('mydb', {adapter: 'idb'});
+console.log(db.adapter); // 'idb'
+```
+
+### [pouchdb-adapter-websql](https://npmjs.org/package/pouchdb-adapter-websql)
+
+An adapter used by PouchDB in the browser, using WebSQL. The adapter
+name is `'websql'`.
+
+Before PouchDB 7.0.0, this was shipped as a default adapter. As of PouchDB 7.0.0, it must be loaded as a separate plugin.
+
+#### Example usage
+
+```bash
+npm install pouchdb-adapter-websql
+```
+
+```js
+PouchDB.plugin(require('pouchdb-adapter-websql'));
+const db = new PouchDB('mydb', {adapter: 'websql'});
+console.log(db.adapter); // 'websql'
+```
+
+### [pouchdb-adapter-leveldb](https://npmjs.org/package/pouchdb-adapter-leveldb)
+
+{% include alert/start.html variant="warning"%}
+{% markdown %}
+**Warning: deprecation notice.** The `leveldb` adapter will be deprecated in PouchDB version 10.0.0 and removed in version 11.0.0. You can read [the migration guide here](https://pouchdb.com/2026/04/10/migration-to-nodesqlite.html) and more about the topic in [this link](https://github.com/apache/pouchdb/issues/9163).
+{% endmarkdown %}
+{% include alert/end.html%}
+
+The primary adapter used by PouchDB in Node.js, using LevelDB. The adapter name
+is `'leveldb'`.
+
+#### Example usage
+
+```bash
+npm install pouchdb-adapter-leveldb
+```
+
+```js
+PouchDB.plugin(require('pouchdb-adapter-leveldb'));
+const db = new PouchDB('mydb', {adapter: 'leveldb'});
+console.log(db.adapter); // 'leveldb'
+```
+
+### [pouchdb-adapter-http](https://npmjs.org/package/pouchdb-adapter-http)
+
+The primary adapter used by PouchDB in both Node.js and the browser for communicating
+with external CouchDB (or CouchDB-like) servers.
+
+This plugin can be added to PouchDB in any order, and is somewhat special in that
+you must pass in a name like `'http://...'` in order to use it. The adapter name
+is either `'http'` or `'https'` depending on the protocol.
+
+#### Example usage
+
+```bash
+npm install pouchdb-adapter-http
+```
+
+```js
+PouchDB.plugin(require('pouchdb-adapter-http'));
+const db = new PouchDB('http://127.0.0.1:5984/mydb');
+console.log(db.adapter); // 'http'
+```
+
+### [pouchdb-adapter-memory](https://npmjs.org/package/pouchdb-adapter-memory)
+
+{% include alert/start.html variant="warning"%}
+{% markdown %}
+**Warning: deprecation notice.** The `memory` adapter will be deprecated in PouchDB version 10.0.0 and removed in version 11.0.0. You can read [the migration guide here](https://pouchdb.com/2026/04/10/migration-to-nodesqlite.html) and more about the topic in [this link](https://github.com/apache/pouchdb/issues/9163).
+{% endmarkdown %}
+{% include alert/end.html%}
+
+An optional adapter that works in the browser and Node.js, fully in-memory. The adapter name
+is `'memory'`.
+
+#### Example usage
+
+```bash
+npm install pouchdb-adapter-memory
+```
+
+```js
+PouchDB.plugin(require('pouchdb-adapter-memory'));
+const db = new PouchDB('mydb', {adapter: 'memory'});
+console.log(db.adapter); // 'memory'
+```
+
+### [pouchdb-adapter-localstorage](https://npmjs.org/package/pouchdb-adapter-localstorage)
+
+An optional adapter that works in the browser using LocalStorage. The adapter name
+is `'localstorage'`.
+
+#### Example usage
+
+```bash
+npm install pouchdb-adapter-localstorage
+```
+
+```js
+PouchDB.plugin(require('pouchdb-adapter-localstorage'));
+const db = new PouchDB('mydb', {adapter: 'localstorage'});
+console.log(db.adapter); // 'localstorage'
+```
+
+### [pouchdb-adapter-node-websql](https://npmjs.org/package/pouchdb-adapter-node-websql)
+
+An optional adapter that works in Node.js using SQLite via [node-websql](https://github.com/nolanlawson/node-websql). The adapter name
+is `'websql'`.
+
+#### Example usage
+
+```bash
+npm install pouchdb-adapter-node-websql
+```
+
+```js
+PouchDB.plugin(require('pouchdb-adapter-node-websql'));
+const db = new PouchDB('mydb', {adapter: 'websql'});
+console.log(db.adapter); // 'websql'
+```
+
+### [pouchdb-adapter-indexeddb](https://npmjs.org/package/pouchdb-adapter-indexeddb)
+
+{% include alert/start.html variant="warning"%}
+{% markdown %}
+
+**Warning: experimental.** You probably don't want to use this yet. 😉
+
+{% endmarkdown %}
+{% include alert/end.html%}
+
+An experimental next-generation IndexedDB adapter, also known as "idb-next". Currently not shipped as part of PouchDB. The adapter name is `'indexeddb'`.
+
+#### Example usage
+
+```bash
+npm install pouchdb-adapter-indexeddb
+```
+
+```js
+PouchDB.plugin(require('pouchdb-adapter-indexeddb'));
+const db = new PouchDB('mydb', {adapter: 'indexeddb'});
+console.log(db.adapter); // 'indexeddb'
+```
+
+### [pouchdb-find](https://npmjs.org/package/pouchdb-find)
+
+PouchDB's "Mango" query API, exposed via the `find()`, `listIndexes(), `createIndex()`, and `deleteIndex()` methods. Not shipped by default in PouchDB.
+
+#### Example usage
+
+```bash
+npm install pouchdb-find
+```
+
+```js
+PouchDB.plugin(require('pouchdb-find'));
+const db = new PouchDB('mydb');
+db.find(/* see API docs for full info */);
+```
+
+### [pouchdb-mapreduce](https://npmjs.org/package/pouchdb-mapreduce)
+
+PouchDB's map/reduce API, exposed via the `query()` and `viewCleanup()` methods. Ships by default in PouchDB.
+
+#### Example usage
+
+```bash
+npm install pouchdb-mapreduce
+```
+
+```js
+PouchDB.plugin(require('pouchdb-mapreduce'));
+const db = new PouchDB('mydb');
+db.query(/* see query API docs for full info */);
+```
+
+### [pouchdb-replication](https://npmjs.org/package/pouchdb-replication)
+
+PouchDB's replication API, exposed via the `replicate()` and `sync()` methods. Ships by default in PouchDB.
+
+#### Example usage
+
+```bash
+npm install pouchdb-replication
+```
+
+```js
+PouchDB.plugin(require('pouchdb-replication'));
+const db = new PouchDB('mydb');
+db.replicate(/* see replicate/sync API docs for full info */);
+```
+
+{% include anchor.html class="h2" title="Utilities" hash="utilities" %}
+
+These utilities are intended only for advanced users of PouchDB, such as
+third-party plugin authors. Formerly, many of them were exposed via the `extras/` API, which
+is now deprecated.
+
+Most of these are internal, and the APIs are not thoroughly documented. You will
+most likely need to read the source code to understand how they work.
+
+{% include alert/start.html variant="warning"%}
+{% markdown %}
+
+**Warning:** you are entering a semver-free zone.
+
+In contrast to the presets and plugins listed above, **none of the following packages
+follow semver**. Their versions are pinned to PouchDB's, and may change at any time
+without warning. You are strongly recommended to **use exact versions** when installing these packages.
+
+{% endmarkdown %}
+{% include alert/end.html%}
+
+### [pouchdb-abstract-mapreduce](https://npmjs.org/package/pouchdb-abstract-mapreduce)
+
+The underlying logic for secondary indexes, as expressed in both `pouchdb-mapreduce` and `pouchdb-find`. Both packages use this package under the hood.
+
+#### Example usage
+
+```bash
+npm install --save-exact pouchdb-abstract-mapreduce
+```
+
+### [pouchdb-adapter-utils](https://npmjs.org/package/pouchdb-adapter-utils)
+
+Utilities for PouchDB adapters.
+
+#### Example usage
+
+```bash
+npm install --save-exact pouchdb-adapter-utils
+```
+
+### [pouchdb-ajax](https://npmjs.org/package/pouchdb-ajax)
+
+PouchDB's `ajax()` function.
+
+#### Example usage
+
+```bash
+npm install --save-exact pouchdb-ajax
+```
+
+### [pouchdb-binary-utils](https://npmjs.org/package/pouchdb-binary-utils)
+
+Utilities for operating on binary strings and Buffers/Blobs.
+
+#### Example usage
+
+```bash
+npm install --save-exact pouchdb-binary-utils
+```
+
+### [pouchdb-checkpointer](https://npmjs.org/package/pouchdb-checkpointer)
+
+Tool to write a checkpoint, e.g. during replication.
+
+#### Example usage
+
+```bash
+npm install --save-exact pouchdb-checkpointer
+```
+
+### [pouchdb-collate](https://npmjs.org/package/pouchdb-collate)
+
+Collation functions for PouchDB map/reduce.
+
+#### Example usage
+
+```bash
+npm install --save-exact pouchdb-collate
+```
+
+### [pouchdb-collections](https://npmjs.org/package/pouchdb-collections)
+
+ES6 shims for Map and Set as used in PouchDB.
+
+#### Example usage
+
+```bash
+npm install --save-exact pouchdb-collections
+```
+
+### [pouchdb-errors](https://npmjs.org/package/pouchdb-errors)
+
+Errors exposed by PouchDB.
+
+#### Example usage
+
+```bash
+npm install --save-exact pouchdb-errors
+```
+
+### [pouchdb-generate-replication-id](https://npmjs.org/package/pouchdb-generate-replication-id)
+
+Function to generate a replication ID to mark progress during replications.
+
+#### Example usage
+
+```bash
+npm install --save-exact pouchdb-generate-replication-id
+```
+
+### [pouchdb-json](https://npmjs.org/package/pouchdb-json)
+
+Utilities for safely stringifying and parsing JSON.
+
+#### Example usage
+
+```bash
+npm install --save-exact pouchdb-json
+```
+
+### [pouchdb-mapreduce-utils](https://npmjs.org/package/pouchdb-mapreduce-utils)
+
+Utilities used by `pouchdb-mapreduce`.
+
+#### Example usage
+
+```bash
+npm install --save-exact pouchdb-mapreduce-utils
+```
+
+### [pouchdb-md5](https://npmjs.org/package/pouchdb-md5)
+
+Utilities for calculating MD5 checksums.
+
+#### Example usage
+
+```bash
+npm install --save-exact pouchdb-md5
+```
+
+### [pouchdb-merge](https://npmjs.org/package/pouchdb-merge)
+
+PouchDB's CouchDB-style document merge algorithm.
+
+#### Example usage
+
+```bash
+npm install --save-exact pouchdb-merge
+```
+
+### [pouchdb-promise](https://npmjs.org/package/pouchdb-promise)
+
+A `Promise` object, polyfilled using `lie` if Promises aren't available globally.
+
+#### Example usage
+
+```bash
+npm install --save-exact pouchdb-promise
+```
+
+### [pouchdb-selector-core](https://npmjs.org/package/pouchdb-selector-core)
+
+The core Mango selector logic, which allows the selector to be used both by `pouchdb-find` and for filtering/replication.
+
+#### Example usage
+
+```bash
+npm install --save-exact pouchdb-selector-core
+```
+
+### [pouchdb-utils](https://npmjs.org/package/pouchdb-utils)
+
+A potpourri of miscellaneous utilities used by PouchDB and its sub-packages.
+
+#### Example usage
+
+```bash
+npm install --save-exact pouchdb-utils
+```
+
+### [sublevel-pouchdb](https://npmjs.org/package/sublevel-pouchdb)
+
+Fork of [level-sublevel](https://github.com/dominictarr/level-sublevel)
+with only the subset of the API that PouchDB uses.
+
+#### Example usage
+
+```bash
+npm install --save-exact sublevel-pouchdb
+```
diff --git a/docs/version/8.0.0/download.md b/docs/version/8.0.0/download.md
new file mode 100644
index 0000000000..a461e55bc3
--- /dev/null
+++ b/docs/version/8.0.0/download.md
@@ -0,0 +1,75 @@
+---
+layout: 2ColLeft.html
+title: Download
+sidebar: ./nav.html
+---
+
+{% include anchor.html class="h3" title="Quick Start" hash="file" %}
+
+{% highlight html %}
+
+
+{% endhighlight %}
+
+PouchDB can also be directly downloaded:
+
+* [pouchdb-{{ site.version }}.min.js][latest-min] (compressed for production)
+* [pouchdb-{{ site.version }}.js][latest] (uncompressed for debugging)
+
+If you are using PouchDB in Internet Explorer a [Promise](https://www.npmjs.com/package/promise-polyfill) and [Fetch](https://www.npmjs.com/package/whatwg-fetch) polyfill will be needed.
+
+{% include anchor.html class="h3" title="npm" hash="npm" %}
+
+PouchDB can be installed through [npm](https://npmjs.com):
+
+{% highlight bash %}npm install --save pouchdb{% endhighlight %}
+
+After installing, call `require()` to use it:
+
+{% highlight javascript %}
+const PouchDB = require('pouchdb');
+const db = new PouchDB('my_database');
+{% endhighlight %}
+
+PouchDB can be used either in Node or in the browser. A bundler such as [Browserify](https://browserify.org/), [Webpack](https://webpack.github.io/), or [Rollup](https://rollupjs.org/) is needed for browser usage.
+
+#### Browser only
+
+If you're only using PouchDB in the browser, you can use `pouchdb-browser` for
+faster install times:
+
+{% highlight bash %}npm install --save pouchdb-browser{% endhighlight %}
+
+{% highlight javascript %}
+const PouchDB = require('pouchdb-browser');
+const db = new PouchDB('my_database');
+{% endhighlight %}
+
+See [custom builds]({{ site.baseurl }}/custom.html) for more options.
+
+{% include anchor.html class="h3" title="CDNs" hash="cdn" %}
+
+PouchDB is hosted at these CDNs:
+
+* [cdnjs](https://cdnjs.com/libraries/pouchdb)
+* [jsdelivr](https://www.jsdelivr.com/#!pouchdb)
+* [unpkg](https://unpkg.com/pouchdb@{{ site.version }}/dist/)
+
+{% highlight bash %}bower install --save pouchdb{% endhighlight %}
+
+{% include anchor.html class="h3" title="Past releases" hash="past-releases" %}
+
+For past releases and changelog, check out the [Github releases page](https://github.com/apache/pouchdb/releases).
+
+{% include anchor.html class="h3" title="Plugins" hash="plugins" %}
+
+For third-party plugins, see the [plugins page](/external.html).
+
+{% include anchor.html class="h3" title="Custom builds" hash="custom" %}
+
+For custom builds and first-party plugins, see the [custom builds]({{ site.baseurl }}/custom.html) page.
+
+[latest]: https://github.com/apache/pouchdb/releases/download/{{ site.version }}/pouchdb-{{ site.version }}.js
+[latest-min]: https://github.com/apache/pouchdb/releases/download/{{ site.version }}/pouchdb-{{ site.version }}.min.js
diff --git a/docs/version/8.0.0/errors.md b/docs/version/8.0.0/errors.md
new file mode 100644
index 0000000000..9fa2b7ba8e
--- /dev/null
+++ b/docs/version/8.0.0/errors.md
@@ -0,0 +1,321 @@
+---
+layout: 2ColLeft.html
+title: Common Errors
+sidebar: ./nav.html
+---
+
+{% include anchor.html class="h3" title="No `Access-Control-Allow-Origin` header" hash="no_access_control_allow_origin_header" %}
+
+If you see the error:
+
+```bash
+XMLHttpRequest cannot load [...]
+No 'Access-Control-Allow-Origin' header is present on the requested resource.
+Origin [...] is therefore not allowed access.
+```
+
+or this one:
+
+```bash
+Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at http://[couchDBIP]:[couchDBPort]/[dbname]/?_nonce=[request hash]. This can be fixed by moving the resource to the same domain or enabling CORS
+```
+
+it's because you need to enable [CORS](https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS) on CouchDB/Cloudant/whatever you're using. Otherwise, your scripts can only access the server database if they're served from the same origin — the protocol (ex: _http://_, _https://_), domain, and port number must match.
+
+You can enable CORS in CouchDB using `curl` or the Futon web interface, but we've saved you some time by making a Node script called [add-cors-to-couchdb](https://github.com/pouchdb/add-cors-to-couchdb). Just run:
+
+{% highlight bash %}
+$ npm install -g add-cors-to-couchdb
+$ add-cors-to-couchdb
+{% endhighlight %}
+
+Or if your database is not at `127.0.0.1:5984`:
+
+{% highlight bash %}
+$ add-cors-to-couchdb http://me.example.com \
+ -u myusername -p mypassword
+{% endhighlight %}
+
+You can check that CORS is now enabled by visiting [http://localhost:5984/_utils/config.html](http://localhost:5984/_utils/config.html) in your browser. You should see something like this:
+
+{% include img.html src="cors_in_couchdb.png" alt="CORS settings in CouchDB" %}
+
+{% include anchor.html class="h3" title="PouchDB throws a `No valid adapter found` error" hash="no_valid_adapter" %}
+
+Reading from/writing to a local database from an `iframe` with a different origin will cause PouchDB to throw a `No valid adapter found` error in Firefox. This is due to Firefox's IndexedDB implementation.
+
+IndexedDB has a same-origin restriction. Read/write operations from another origin will always fail, but only Firefox triggers a `No valid adapter found` error. Chrome / Opera will instead throw an [`UnknownError`](#unknown_error_chrome).
+
+{% include anchor.html class="h3" title="iOS/Safari: \"there was not enough remaining storage space\"" hash="not_enough_space" %}
+
+On iOS and Safari, if you expect your app to use more than 5MB of space, you will need to request the space up-front from the user. In certain versions of Safari, you can never request more than what the user originally grants you.
+
+{% include img.html src="safari_popup.png" alt="Safari storage quota popup" %}
+
+To get around this, when you create your PouchDB, use the `opts.size` option for the expected _maximum_ size of the database in MB. Valid increments are 10, 50, 100, 500, and 1000. For instance, if you request 50, then Safari will show a popup saying "allow 50MB?" but if you request 51, then Safari will show a popup saying "allow 100MB?".
+
+If you don't use the `size` option, then you'll be able to use up to 5MB without any popup, but then once you use more, there will be a popup asking for 10.
+
+```js
+new PouchDB('mydb', {size: 10}); // request 10 MB with a popup
+new PouchDB('mydb', {size: 50}); // request 50 MB with a popup
+new PouchDB('mydb'); // implicitly request 5 MB, no popup until you exceed 5MB
+```
+
+This does not affect any backend other than Web SQL. Chrome, Android, and Opera do not show the popup. On PhoneGap/Cordova apps, you can also use the [SQLite plugin][sqlite] to get around this problem. Here's [more information about storage quotas](http://www.html5rocks.com/en/tutorials/offline/quota-research) and [details on the Safari/iOS bug](https://github.com/apache/pouchdb/issues/2347).
+
+{% include anchor.html class="h3" title="PouchDB throws 404 (Object Not Found) for '_local' document" hash="404__local_document" %}
+
+Don't worry, nothing is amiss, this is expected behaviour:
+During PouchDB's initial replication PouchDB will check for a checkpoint, if it doesn't exist a 404 will be returned and a checkpoint will subsequently be written.
+
+{% include anchor.html class="h3" title="PouchDB is throwing `InvalidStateError`" hash="throwing_invalidstateerror" %}
+
+Are you in private browsing mode? IndexedDB is [disabled in private browsing mode](https://developer.mozilla.org/en-US/docs/IndexedDB/Using_IndexedDB) in Firefox.
+
+{% include anchor.html class="h3" title="Can't open second database on Android WebView with Cordova/Phone Gap" hash="phonegap_cordova_second_database" %}
+
+There is a limit of one database per app in some versions of the Android WebView. Install the [SQLite plugin][sqlite], then PouchDB will use that if it is available.
+
+{% include anchor.html class="h3" title="Possible EventEmitter memory leak detected" hash="event_emitter_limit" %}
+
+If you see this warning:
+
+```bash
+(node) warning: possible EventEmitter memory leak detected. 11 listeners added.
+Use emitter.setMaxListeners() to increase limit.
+```
+
+This is because PouchDB uses Node-style [EventEmitters](https://nodejs.org/api/events.html) for its events. An EventEmitter is any object that has an `.on()` or `once()` method, such as `db.changes().on('change', ...`.
+
+By default, all EventEmitters have 10 listeners, and if you exceed that limit, e.g. by attaching many `changes()` listeners or running many simultaneous `replicate()` or `sync()` events, then you may exceed this limit.
+
+**This could indicate a memory leak in your code**. Check to make sure that you are calling `cancel()` on any `changes()`, `replicate()`, or `sync()` handlers, if you are constantly starting and stopping those events.
+
+If you're sure it's not a memory leak, though, you can increase the limit by doing:
+
+{% highlight javascript %}
+db.setMaxListeners(20); // or 30 or 40 or however many you need
+{% endhighlight %}
+
+In the above example, `db` refers to a database object you created using `new PouchDB('dbname')`.
+
+{% include anchor.html class="h3" title="Database size limitation of ~5MB on iOS with Cordova/Phone Gap" hash="size_limitation_5mb" %}
+
+If you're storing large amounts of data, such as PNG attachments, the [SQLite plugin][sqlite] is again your friend. (See [issue #1260](https://github.com/apache/pouchdb/issues/1260) for details.)
+
+{% include anchor.html class="h3" title="CouchDB returns a 404 for GETs from a CouchApp" hash="404_get_couchapp" %}
+
+Certain URL rewrites are broken by PouchDB's cache-busting; try adding `{cache : false}` to the PouchDB constructor. (See [issue #1233](https://github.com/apache/pouchdb/issues/1233) for details.)
+
+{% include anchor.html class="h3" title="Uncaught TypeError: object is not a function" hash="typeerror_object_is_not_a_function" %}
+
+Did you include the [es6-promise shim library](https://github.com/jakearchibald/es6-promise)? Not every browser implements ES6 Promises correctly. (See [issue #1747](https://github.com/apache/pouchdb/issues/1747) for details.)
+
+{% include anchor.html class="h3" title="SyntaxError: Parse error (Cordova on Android)" hash="cordova_android_parse_error" %}
+
+Did you include the [es5-shim library][es5shim]? PouchDB is written in ES5, which is supported by modern browsers, but requires shims for older browsers (e.g. IE 9, Android 2.1 WebView).
+
+In Android, if you're loading PouchDB directly via `webView.loadUrl('javascript://' + js')`, you should prefer the minified PouchDB javascript file to the unminified one, since code comments can also cause parse errors.
+
+{% include anchor.html class="h3" title="PouchDB object fails silently (Safari)" hash="safari_object_silent_fail" %}
+
+Safari requires users to confirm that they want to allow an app to store data locally ("Allow this website to use space on your disk?"). If PouchDB is loaded in an `iframe` or some other unusual way, the dialog might not be shown, and the database will silently fail.
+
+{% include anchor.html class="h3" title="window.localStorage is not available (Chrome apps)" hash="window_localstorage_chrome_apps" %}
+
+In Chrome apps, you'll see the warning "window.localStorage is not available in packaged apps. Use chrome.storage.local instead." This is harmless; since PouchDB doesn't use localStorage if it's not available.
+
+{% include anchor.html class="h3" title="Error: UnknownError (Firefox)" hash="unknown_error_ff" %}
+
+Are you using a webserver to host and run your code? This error can be caused by running your script/file locally using the `file:///` setting in Firefox, since Firefox does not [allow access to IndexedDB locally](https://bugzilla.mozilla.org/show_bug.cgi?id=643318). You can use the SimpleHTTPServer to deploy your script by running `python -m SimpleHTTPServer` from the directory containing the script, or use the Apache webserver and then access the script by using `http://localhost/{path_to_your_script}`.
+
+{% include anchor.html class="h3" title="Error: UnknownError (Chrome / Opera)" hash="unknown_error_chrome" %}
+
+This can occur when attempting to read from or write to IndexedDB from a different origin. IndexedDB has a same-origin restriction. Attempting to write to the database associated with _http://example.com_ from an `iframe` served from _http://api.example.com_, for example, will fail.
+
+In Firefox, PouchDB instead throws a [`No valid adapter found`](#no_valid_adapter) error.
+
+{% include anchor.html class="h3" title="DataCloneError: An object could not be cloned" hash="could_not_be_cloned" %}
+
+If you ever see:
+
+```bash
+Uncaught DataCloneError:
+ Failed to execute 'put' on 'IDBObjectStore':
+ An object could not be cloned.
+```
+
+Or:
+
+```bash
+DataCloneError: The object could not be cloned.
+```
+
+Then the problem is that the document you are trying to store is not a pure JSON object. For example, an object with its own class (`new Foo()`) or with special methods like getters and setters cannot be stored in PouchDB/CouchDB.
+
+If you are ever unsure, then run this on the document:
+
+```js
+JSON.parse(JSON.stringify(myDocument));
+```
+
+If the object you get out is the same as the object you put in, then you are storing the right kind of object.
+
+Note that this also means that you cannot store `Date`s in your document. You must convert them to strings or numbers first. `Date`s will be stored as-is in IndexedDB, but in the other adapters and in CouchDB, they will be automatically converted to ISO string format, e.g. `'2015-01-01T12:00:00.000Z'`. This can caused unwanted results. See [#2351](https://github.com/apache/pouchdb/issues/2351) and [#2158](https://github.com/apache/pouchdb/issues/2158) for details.
+
+{% include anchor.html class="h3" title="DOM Exception 18 in Android pre-Kitkat WebView" hash="android_pre_kitkat" %}
+
+This applies to hybrid apps designed to run in Android pre-Kitkat (i.e. before 4.4).
+
+If you are directly using a `WebView` and not using Cordova/PhoneGap, you will probably either run into an error where PouchDB silently fails or you see `Error: SECURITY_ERR: DOM Exception 18` in the console. As a sanity test, you can run this JavaScript:
+
+```js
+openDatabase('mydatabase', 1, 'mydatabase', 5000000, function (db) { console.log('it works!'); });
+```
+
+If you see "it works" in the console, then everything's peachy. Otherwise there are a few things you have to do.
+
+First, make sure Web SQL is enabled on your `WebView` in the first place using [setDatabaseEnabled](http://developer.android.com/reference/android/webkit/WebSettings.html#setDatabaseEnabled%28boolean%29):
+
+```java
+myWebView.getSettings().setDatabaseEnabled(true);
+```
+
+Second, specify a path for the database. Yes, you need to do this, even though it's deprecated in Kitkat:
+
+```java
+String databasePath = getContext().getApplicationContext().getDir(
+ "database", Context.MODE_PRIVATE).getPath();
+webView.getSettings().setDatabasePath(databasePath);
+```
+
+Third, you'll need to set an `onExceededDatabaseQuota` handler. Yes, it's also deprecated in Kitkat. Yes, you still need to do it.
+
+```java
+webView.setWebChromeClient(new WebChromeClient() {
+
+ @Override
+ public void onExceededDatabaseQuota(String url, String databaseIdentifier, long currentQuota, long estimatedSize,
+ long totalUsedQuota, WebStorage.QuotaUpdater quotaUpdater) {
+ quotaUpdater.updateQuota(estimatedSize * 2);
+ }
+});
+```
+
+If you skip any one of these three steps, then you will get the `DOM Exception 18` error. You need to do all three.
+
+Alternatively, you can also load the `WebView` with a fake `http://` URL, but this may cause other errors when you try to fetch files based on a relative path:
+
+```java
+webView.loadDataWithBaseURL("http://www.example.com",
+ htmlContent,
+ "text/html",
+ "utf-8",
+ null);
+```
+
+{% include anchor.html class="h3" title="Replication with attachments is slow or fails" hash="replicating_attachments_slow" %}
+
+The symptoms for this issues are:
+
+1. Replicating a database that has many attachments from a CouchDB server is either slow or fails randomly.
+2. You get server error message of the nature `No buffer space available`.
+
+Chances are that your server runs inside a virtual machine. The host system, or hypervisor, imposes limits on how much data each virtual machine can use for networking. If you are on a cheap virtual server, it is possible, that the default settings for PouchDB pull-replication (10 parallel batches of 100 documents each) exhaust the narrow limit of your server. Even a single client can cause this.
+
+The solution is to move to a better server, but if that is not an immediate option, a workaround would be reducing the `options.batch_size` and `options.batches_limit` [replication options]({{ site.baseurl }}/api.html#replication).
+
+To find optimal values, start by setting them both to 1 (meaning that PouchDB should download one document after the other) and increase from there and stop when the symptoms begin again. Note that multiple concurrent clients can still cause an issue, if you get too many. If all your documents have one or more attachments (e.g. a photos database), setting both options to `1` is probably a good idea.
+
+{% include alert/start.html variant="info" %}
+
+Generally, reducing these options that replicating the database down will take more time. Please test various settings to see what works for you and your hardware.
+
+{% include alert/end.html %}
+
+{% include alert/start.html variant="info" %}
+
+This issue has been found on OpenVZ systems, but other Hypervisors might also be affected. See
+[http://blog.aplikacja.info/105_no_buffer_space_available_on_openvz_vps.html](http://blog.aplikacja.info/105_no_buffer_space_available_on_openvz_vps.html)
+on how to diagnose this issue.
+
+{% include alert/end.html %}
+
+[es5shim]: https://github.com/es-shims/es5-shim
+[sqlite]: https://github.com/brodysoft/Cordova-SQLitePlugin
+
+{% include anchor.html class="h3" title="Packaging PouchDB in an app with WebPack" hash="package_pouchdb_webpack" %}
+
+PouchDB may have various dependencies that may not play nicely with WebPack. Here are some issues you may run into and their resolutions:
+
+**You may need an appropriate loader to handle this file type.**
+
+If you run into the following error (or similar):
+
+```bash
+ERROR in ./~/pouchdb/~/levelup/package.json
+Module parse failed: /path/to/node_modules/pouchdb/node_modules/levelup/package.json Line 2: Unexpected token :
+You may need an appropriate loader to handle this file type.
+| {
+| "name": "levelup",
+| "description": "Fast & simple storage - a Node.js-style LevelDB wrapper",
+| "version": "0.18.6",
+ @ ./~/pouchdb/~/levelup/lib/util.js 102:30-56
+
+```
+WebPack needs to be configured to recognize how to load json files. Simply, install `json-loader` and edit `webpack.config.js` as follows:
+
+```js
+module: {
+ loaders: [
+ // https://github.com/apache/pouchdb/issues/3319
+ {
+ test: /\.json$/,
+ loader: "json-loader"
+ }
+ ]
+}
+```
+
+{% include anchor.html class="h3" title="Failed to load resource: the server responded with a status of 400 (Bad request) " hash="couchbase_dbname" %}
+
+If you are using Couchbase Lite to sync with PouchDB then you cannot use capital letters in your database name as Couchbase Lite has restrictions on valid database names.
+
+{% include anchor.html class="h3" title="Live changes pass undetected (Web Extension)" hash="webextension_live_changes" %}
+
+When using PouchDB in a WebExtension (at least in Chromium 55 and Firefox 50), but apparently only if the `manifest.json` contains the `store` permission, a [live changes feed]({{ site.baseurl }}/guides/changes.html#live-changes-feed) in one tab or background process may not detect changes made in another tab/process. It will of course still report those changes upon the next change that it does detect.
+Minimal code to reproduce this can be found [here](https://gist.github.com/Treora/150dca4b57b1b881bd049303e82c5ced).
+
+{% include anchor.html class="h3" title="PouchDB install errors on Windows" hash="pouchdb_install_errors_windows" %}
+
+Sometimes `npm install pouchdb` fails on windows, when no prebuilt binary is available. The error looks similar to this:
+
+```bash
+C:\XXX\node_modules\project_name>if not defined npm_config_node_gyp (node "C:\XXX\node_modules\npm\bin\node-gyp-bin\....\node_modules\node-gyp\bin\node-gyp.js" rebuild ) else (node "" rebuild )
+gyp ERR! configure error
+gyp ERR! stack Error: Can't find Python executable "python", you can set the PYTHON env variable.
+gyp ERR! stack at PythonFinder.failNoPython (C:\XXX\nodejs\node_modules\npm\node_modules\node-gyp\lib\configure.js:483:19)
+```
+
+or something like this
+
+```bash
+gyp ERR! configure error
+npm ERR! code ELIFECYCLE
+npm ERR! errno 1
+npm ERR! leveldown@3.0.0 install: `prebuild-install || node-gyp rebuild`
+npm ERR! Exit status 1
+npm ERR!
+npm ERR! Failed at the leveldown@3.0.0 install script.
+```
+
+If you are on windows and getting any error messages mentioning **leveldown** and **Python**, please attempt the steps below.
+
+Node-Gyp requires Python 2.7, it does not work with Python 3.x unfortunately.
+
+Here are a few steps you can take:
+
+1. try `npm install --global --production windows-build-tools` (this command needs to be ran with admin privileges)
+2. read other [pointers to using node-gyp on Windows here](https://github.com/nodejs/node-gyp#on-windows)
+3. If you are only using PouchDB in the browser, you can install [pouchdb-browser](https://www.npmjs.com/package/pouchdb-browser) instead: `npm install --save pouchdb-browser`
diff --git a/docs/version/8.0.0/external.md b/docs/version/8.0.0/external.md
new file mode 100644
index 0000000000..95cbc41816
--- /dev/null
+++ b/docs/version/8.0.0/external.md
@@ -0,0 +1,306 @@
+---
+layout: 2ColLeft.html
+title: Plugins and External Projects
+sidebar: ./nav.html
+---
+
+Below is a list of known plugins, tools and projects can be used with PouchDB.
+
+## {% include anchor.html title="Plugins" hash="plugins" %}
+
+#### [PouchDB allDbs()](https://github.com/nolanlawson/pouchdb-all-dbs)
+
+Revives the `allDbs()` function, which lists all PouchDB databases.
+
+#### [PouchDB Authentication](https://github.com/nolanlawson/pouchdb-authentication)
+
+Plugin for CouchDB's authentication system.
+
+#### [Pouch Box](https://github.com/jo/pouch-box)
+
+Allows decentralized authentication and access control per document, using asymmetric encryption.
+
+#### [PouchDB Collate](https://github.com/apache/pouchdb/tree/master/packages/node_modules/pouchdb-collate)
+
+Collation functions for PouchDB map/reduce. Used by PouchDB map/reduce to maintain consistent [CouchDB collation ordering](https://wiki.apache.org/couchdb/View_collation).
+
+#### [Crypto Pouch](https://github.com/calvinmetcalf/crypto-pouch)
+
+Encrypt a PouchDB/CouchDB database.
+
+#### [Pouch Dat](https://github.com/calvinmetcalf/pouch-dat)
+
+Replicate from PouchDB to Dat.
+
+#### [PouchDB Hyperbee](https://github.com/RangerMauve/pouchdb-adapter-hyperbee)
+
+Sparsely load data from Hyperbee over p2p networks in Node and the Browser.
+
+#### [Pouch Datalog](https://github.com/dahjelle/pouch-datalog)
+
+Implement the Datalog query language on PouchDB, with indexes.
+
+#### [PouchDB Dump](https://github.com/nolanlawson/pouchdb-dump-cli) and [PouchDB Load](https://github.com/nolanlawson/pouchdb-load)
+
+Dump a PouchDB/CouchDB to a file, then load it wholesale. Designed for fast initial replication.
+
+#### [Delta Pouch](https://github.com/redgeoff/delta-pouch)
+
+Implements the handy "every document is a delta" pattern, so you don't have to deal with conflicts.
+
+#### [PouchDB Erase](https://github.com/marten-de-vries/pouchdb-erase)
+
+A replicating `db.destroy()` alternative.
+
+#### [PouchDB Full Sync](https://github.com/nolanlawson/pouchdb-full-sync)
+
+Fully replicate two PouchDB/CouchDB databases, preserving absolutely all revision history.
+
+#### [PouchDB GQL](https://github.com/pouchdb/GQL)
+
+Google Query Language (GQL) queries with PouchDB.
+
+#### [PouchDB Hoodie API](https://github.com/hoodiehq/pouchdb-hoodie-api)
+
+Hoodie-like API for PouchDB. ([Documentation](http://hoodiehq.github.io/pouchdb-hoodie-api/))
+
+#### [PouchDB Hoodie Store Client](https://www.npmjs.com/package/@hoodie/store-client)
+
+PouchDB Hoodie-like API for data persistence & offline sync.
+
+#### [PouchDB List](http://python-pouchdb.marten-de-vries.nl/plugins.html)
+
+Allows you to re-use your CouchDB list functions on the client side. ([Documentation](http://pythonhosted.org/Python-PouchDB/js-plugins.html#pouchdb-rewrite-plug-in))
+
+#### [Pouch Mirror](https://github.com/colinskow/pouch-mirror)
+
+Creates a synced in-memory mirror of a remote CouchDB for faster reads.
+
+#### [PouchDB No-Eval Map/Reduce](https://github.com/evidenceprime/pouchdb.mapreduce.noeval)
+
+Allows you to use the `query()` API in environments that disallow `eval()`, like Chrome packaged apps.
+
+#### [Peer Pouch](https://github.com/natevw/PeerPouch)
+
+PouchDB over WebRTC. (Note: only works with PouchDB 1.1.)
+
+#### [PouchDB Resolve Conflicts](https://github.com/jo/pouch-resolve-conflicts)
+
+Plugin to assist in PouchDB conflict resolving.
+
+#### [PouchDB Migrate](https://github.com/eHealthAfrica/pouchdb-migrate)
+
+PouchDB plugin for running data migrations.
+
+#### [PouchDB Quick Search](https://github.com/nolanlawson/pouchdb-quick-search)
+
+Full-text search engine on top of PouchDB.
+
+#### [Relational Pouch](https://github.com/nolanlawson/relational-pouch)
+
+A relational database API on top of PouchDB/CouchDB.
+
+#### [PouchDB Replication Stream](https://github.com/nolanlawson/pouchdb-replication-stream)
+
+Replicate between CouchDB/PouchDB using streams.
+
+#### [PouchDB Rewrite](http://python-pouchdb.marten-de-vries.nl/plugins.html)
+
+A PouchDB plugin that allows you to re-use your CouchDB rewrites on the client side. ([Documentation](http://pythonhosted.org/Python-PouchDB/js-plugins.html#pouchdb-list-plug-in))
+
+#### [PouchDB Show](http://python-pouchdb.marten-de-vries.nl/plugins.html)
+
+A PouchDB plugin that allows you to re-use your CouchDB show functions on the client side. ([Documentation](http://pythonhosted.org/Python-PouchDB/js-plugins.html#pouchdb-show-plug-in))
+
+#### [SocketPouch](https://github.com/nolanlawson/socket-pouch)
+
+PouchDB/CouchDB replication over WebSockets, using Engine.io (Socket.io).
+
+#### [PouchDB Spatial](https://github.com/pouchdb/geopouch)
+
+Multidimensional and spatial queries with PouchDB.
+
+#### [PouchDB Geospatial](https://github.com/dpmcmlxxvi/pouchdb-geospatial)
+
+PouchDB geospatial querying of GeoJSON objects that supports the DE-9IM spatial predicates. ([Documentation](https://dpmcmlxxvi.github.io/pouchdb-geospatial/api/))
+
+#### [Superlogin](https://www.npmjs.com/package/superlogin)
+
+Powerful authentication for APIs and single page apps using the CouchDB ecosystem, which supports a variety of providers.
+
+#### [Store.PouchDB](https://github.com/chunksnbits/store.pouchdb)
+
+ORM-style storage plugin for PouchDB.
+
+#### [Pouch Stream](https://github.com/calvinmetcalf/PouchStream)
+
+A plugin to let PouchDB talk streams.
+
+#### [Transform Pouch](https://github.com/nolanlawson/transform-pouch)
+
+Transforms documents before and after storage, e.g. for encryption, compression, or massaging data.
+
+
+#### [PouchDB Update](http://python-pouchdb.marten-de-vries.nl/plugins.html)
+
+A PouchDB plugin that allows you to re-use your CouchDB update functions on the client side. ([Documentation](http://pythonhosted.org/Python-PouchDB/js-plugins.html#pouchdb-update-plug-in))
+
+#### [PouchDB Upsert](https://github.com/nolanlawson/pouchdb-upsert)
+
+Convenience functions for working with documents: `upsert()` and `putIfNotExists()`.
+
+#### [PouchDB Validation](http://python-pouchdb.marten-de-vries.nl/plugins.html)
+
+A PouchDB plugin that allows you to re-use your CouchDB `validate_doc_update` functions on the client side. ([Documentation](http://pythonhosted.org/Python-PouchDB/js-plugins.html#pouchdb-validation-plug-in))
+
+#### [WorkerPouch](http://github.com/nolanlawson/worker-pouch)
+
+PouchDB adapter for web workers, so that PouchDB blocks the DOM less.
+
+{% include anchor.html title="Server Side" hash="Server Side" %}
+
+#### [PouchDB Server](https://github.com/pouchdb/pouchdb-server)
+
+A standalone CouchDB-style REST interface server to PouchDB.
+
+#### [Express PouchDB](https://github.com/pouchdb/express-pouchdb)
+
+An Express submodule with a CouchDB-style REST interface to PouchDB. Powers PouchDB Server.
+
+#### [Express PouchDB Replication Stream](https://github.com/conor-mac-aoidh/express-pouchdb-replication-stream)
+
+Server-side Express endpoint to deliver a stream from [PouchDB Replication Stream](https://github.com/nolanlawson/pouchdb-replication-stream).
+
+#### [Howler](https://github.com/redgeoff/couchdb-howler)
+
+Use web sockets to subscribe to CouchDB global changes
+
+#### [Pouch Websocket Sync](https://github.com/pgte/pouch-websocket-sync)
+
+Sync several PouchDBs through websockets. Supports reconnection, negotiation and authentication.
+
+#### [Pouch Remote Stream](https://github.com/pgte/pouch-remote-stream#readme)
+
+Consume a remote PouchDB stream. Goes well with [pouch-stream-server](https://github.com/pgte/pouch-stream-server#readme) on the server side.
+
+#### [Pouch Stream Server](https://github.com/pgte/pouch-stream-server#readme)
+
+PouchDB stream server. Serves generic PouchDB object streams. Goes well with [pouch-remote-stream](https://github.com/pgte/pouch-remote-stream#readme) on the client.
+
+#### [Spiegel](https://github.com/redgeoff/spiegel)
+
+Scalable replication and change listening for CouchDB
+
+{% include anchor.html title="Framework adapters" hash="framework_adapters" %}
+
+### Angular
+
+#### [angular-pouchdb](https://github.com/angular-pouchdb/angular-pouchdb)
+
+Wrapper for using PouchDB within Angular.js.
+
+#### [Factoryng - AngularJS Adapter](https://github.com/redgeoff/factoryng)
+
+An all-in-one AngularJS factory that wraps PouchDB and Delta Pouch.
+
+#### [ngPouch](https://github.com/jrhicks/ngPouch)
+
+Angular service to persist remote connection settings and maintain continuous replication.
+
+#### [ng-pouchdb](https://github.com/danielzen/ng-pouchdb)
+
+AngularJS binding for PouchDB.
+
+### Ampersand
+
+#### [ampersand-collection-pouchdb-mixin](https://github.com/svnlto/ampersand-collection-pouchdb-mixin)
+
+A mixin for extending ampersand-collection with pouchdb persistence.
+
+### Backbone
+
+#### [Backbone PouchDB](https://github.com/jo/backbone-pouch)
+
+Backbone PouchDB Sync Adapter.
+
+### Ember
+
+#### [Ember Pouch](https://github.com/nolanlawson/ember-pouch)
+
+Ember Data adapter for PouchDB/CouchDB.
+
+#### [ember-pouchdb](https://github.com/taras/ember-pouchdb)
+
+Promisy PouchDB wrapper for Ember.js.
+
+### [GopherJS](https://github.com/gopherjs/gopherjs)
+
+#### [Kivik](https://github.com/go-kivik/kivik)
+
+Kivik provides a common interface to CouchDB or CouchDB-like databases for Go and GopherJS. ([PouchDB driver](https://github.com/go-kivik/pouchdb))
+
+### Kendo UI
+
+#### [kendo-pouchdb](https://github.com/terikon/kendo-pouchdb)
+
+Kendo UI DataSource adapter.
+
+### React/Flux
+
+#### [react-pouchdb](https://github.com/ArnoSaine/react-pouchdb)
+
+React wrapper for PouchDB that also subscribes to changes.
+
+#### [pouch-redux](https://github.com/UXtemple/pouch-redux)
+
+Pouch and Redux integration. With Pouch in control this time around.
+
+#### [redux-pouchdb](https://github.com/vicentedealencar/redux-pouchdb)
+
+Sync store state to PouchDB.
+
+#### [redux-pouch](https://github.com/UXtemple/redux-pouch)
+
+PouchDB-backed Redux.
+
+#### [pouch-redux-middleware](https://github.com/pgte/pouch-redux-middleware#readme)
+
+Redux middleware to sync a PouchDB database with the Redux state.
+
+### Vue.js
+
+#### [pouch-vue](https://github.com/MDSLKTR/pouch-vue)
+
+Syncs PouchDB data with Vue.js components using Mango Selectors
+
+#### [vue-pouch-db](https://github.com/QurateInc/vue-pouch-db)
+
+Vue Pouch DB is a VueJS Plugin that binds PouchDB with Vue and keeps a synchronised state with the database. Has support for Mango queries which are processed locally within the VuePouchDB state.
+
+
+{% include anchor.html title="Other languages" hash="Other languages" %}
+
+#### [Python-PouchDB](http://python-pouchdb.marten-de-vries.nl/)
+A Python interface to PouchDB, with both a synchronous and an asynchronous API. Uses QtWebKit internally (via either PySide, PyQt4 or PyQt5). Some PouchDB plugins are also wrapped. ([Documentation](http://pythonhosted.org/Python-PouchDB/) / [Launchpad](https://launchpad.net/python-pouchdb))
+
+#### [PouchDroid](https://github.com/nolanlawson/PouchDroid/)
+
+Android adapter with a native Java interface to PouchDB.
+
+{% include anchor.html title="Tools" hash="Tools" %}
+
+#### [blob-util](https://github.com/nolanlawson/blob-util)
+
+Shims and utils for working with binary Blobs in the browser.
+
+#### [Pouchy](https://www.npmjs.com/package/pouchy)
+
+PouchDB sugar API. ([Github](https://github.com/cdaringe/pouchy))
+
+#### [Puton](http://puton.jit.su/)
+
+A bookmarklet for inspecting PouchDB databases within the browser. ([Github](http://github.com/ymichael/puton))
+
+#### [Revision Tree Visualizer](http://neojski.github.io/visualizeRevTree)
+
+A tool drawing revision tree of a couchdb document. You can see what is a conflict, which revisions are deleted and which is winning. ([Github](https://github.com/neojski/visualizeRevTree))
diff --git a/docs/version/8.0.0/faq.md b/docs/version/8.0.0/faq.md
new file mode 100644
index 0000000000..411ed8fc09
--- /dev/null
+++ b/docs/version/8.0.0/faq.md
@@ -0,0 +1,147 @@
+---
+layout: 2ColLeft.html
+title: FAQ
+sidebar: ./nav.html
+---
+
+{% include anchor.html class="h3" title="Can PouchDB sync with MongoDB/MySQL/my current non-CouchDB database?" hash="sync_non_couchdb" %}
+
+
+No, your backend needs to speak the [CouchDB replication protocol](http://couchdb.readthedocs.org/en/latest/replication/protocol.html). The magic of PouchDB <–> CouchDB sync comes from this design, which in particular requires all documents to be versioned with the `_rev` marker. This allows PouchDB and CouchDB to [elegantly handle conflicts](http://writing.jan.io/2013/12/19/understanding-couchdb-conflicts.html), among other benefits.
+
+{% include anchor.html class="h3" title="What can PouchDB sync with?" hash="what_can_pouchdb_sync_with" %}
+
+There are a number of databases that implement a CouchDB-like protocol, and PouchDB should be able to replicate with them. They include:
+
+ * [CouchDB](http://couchdb.apache.org/) – CouchDB is our primary reference database and is used for automated testing.
+ * [Cloudant](https://cloudant.com/) – A cluster-aware fork of CouchDB.
+ * [PouchDB Server](https://github.com/pouchdb/pouchdb-server) – An HTTP API written on top of PouchDB. Additionally, it supports alternate backends like in-memory, Redis, Riak and MySQL via [the LevelUP ecosystem](https://github.com/rvagg/node-levelup/wiki/Modules#storage). Note that your application must use the PouchDB API rather than directly modifying the database, however.
+
+{% include anchor.html class="h3" title="The web is nice, but I want to build a native app?" hash="native_support" %}
+
+PouchDB is one of multiple projects that implement the CouchDB protocol, and these can all be used to sync the same set of data.
+
+For desktop applications, you may want to look into embedding CouchDB (or [rcouch](https://github.com/refuge/rcouch)). PouchDB also works great with web-based frameworks like [node-webkit](https://github.com/rogerwang/node-webkit), [Chrome apps](https://developer.chrome.com/apps/about_apps), [Electron](https://github.com/atom/electron) and [WinJS](http://try.buildwinjs.com/#listview).
+
+For mobile applications, you can use PouchDB within [PhoneGap](http://phonegap.com/)/[Cordova](http://cordova.apache.org/) (optionally using the [SQLite Plugin](https://github.com/brodysoft/Cordova-SQLitePlugin)), or there are several native libraries:
+
+**iOS**:
+
+* [Couchbase Lite for iOS](https://github.com/couchbase/couchbase-lite-ios)
+* [Cloudant Sync for iOS](https://github.com/cloudant/CDTDatastore)
+
+**Android**:
+
+* [Couchbase Lite for Android](https://github.com/couchbase/couchbase-lite-android)
+* [Cloudant Sync for Android](https://github.com/cloudant/sync-android)
+
+{% include anchor.html class="h3" title="How much data can PouchDB store?" hash="data_limits" %}
+
+In **Firefox**, PouchDB uses IndexedDB. Though Firefox has no upper limit besides disk space, if your application wishes to store more than 50MB locally, Firefox will [ask the user](https://developer.mozilla.org/en-US/docs/Web/API/IndexedDB_API) using a non-modal dialog to confirm that this is okay.
+
+**Chrome** also uses IndexedDB, and it determines the amount of storage available on the user’s hard drive and uses that [to calculate a limit](https://developers.google.com/chrome/whitepapers/storage#temporary).
+
+**Opera 15+** shares a codebase with Chromium/Blink, and behaves similarly.
+
+**Internet Explorer 10+** has a hard 250MB limit, and will prompt the user with a non-modal dialog at 10MB.
+
+**Mobile Safari** on iOS has a hard 50MB limit for WebSQL, whereas **desktop Safari** has no limit. Both will prompt the user with a modal dialog if an application requests more than 5MB of data, at increments of 5MB, 10MB, 50MB, 100MB, etc. Some versions of Safari have a bug where they only let you request additional storage once, so you'll need to request the desired space up-front. PouchDB allows you to do this using [the `size` option]({{ site.baseurl }}/api.html#create_database).
+
+**iOS Web Application**, a page saved on the homescreen behaves differently than apps in Mobile Safari (at least from iOS 9.3.2+). No specifics are published online by Apple, but WebSQL storage seems not limited to 50mb and there will not be any prompts when requesting data storage. Use [``](https://developer.apple.com/library/ios/documentation/AppleApplications/Reference/SafariWebContent/ConfiguringWebApplications/ConfiguringWebApplications.html#//apple_ref/doc/uid/TP40002051-CH3-SW3) in your html header and use *Add to Home Screen* in the share menu of Safari. Please note, IndexedDB is now available as of iOS 10.3. It seems there is different behavior for different models of iPad and iPhone. You can check your mileage using the [storage abuser](http://demo.agektmr.com/storage/), which you can *Add to Home Screen* on your device. **Caveat**: when iOS is running low on storage space, the OS might decide to delete all data without any notice or warning to the end user. Be sure to use devices with plenty of spare space, or your users will lose unsynced data.
+
+**Android** works the same as Chrome as of 4.4+ (IndexedDB), while older versions can store up to 200MB (WebSQL).
+
+In [PhoneGap](http://phonegap.com/)/[Cordova](http://cordova.apache.org/), you can have unlimited data on both iOS and Android by using the [SQLite Plugin](https://github.com/brodysoft/Cordova-SQLitePlugin).
+
+For more information, see [Working with quota on mobile browsers](http://www.html5rocks.com/en/tutorials/offline/quota-research/).
+
+{% include anchor.html class="h3" title="What data types does PouchDB support?" hash="data_types" %}
+
+PouchDB has two types of data: documents and attachments.
+
+#### Documents
+
+As in CouchDB, the documents you store must be serializable as JSON. Modifying the `Object` prototype or storing classes is not supported.
+
+IndexedDB will actually support non-JSON data (e.g. `Date`s aren't stringified), but you should not rely on this, because CouchDB, LevelDB, and Web SQL do not behave the same.
+
+#### Attachments
+
+PouchDB also supports attachments, which are the most efficient way to store binary data. Attachments may either be supplied as base64-encoded strings or as `Blob` objects.
+
+Different backends have different strategies for storing binary data, which may affect the overall database size. Attachment stubs have a `length` property that describes the number of bytes in the `Blob` object, but under the hood, it may actually take up more space than that.
+
+PouchDB's strategies are:
+
+* **Blob**: data is stored in a true binary format. The most efficient method.
+* **UTF-16 Blob**: blobs are coerced to UTF-16, so they takes up 2x the normal space.
+* **Base-64**: data is stored as a base-64-encoded string. The least efficient method.
+
+Here are the strategies used by various browsers in PouchDB:
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Adapter
+
IE (10+)
+
Firefox
+
Chrome < 43, Android
+
Chrome >= 43
+
Safari < 7.1, iOS < 8
+
Safari < 10.1, >= 7.1, iOS < 10.3, >= 8
+
Safari >= 10.1, iOS >= 10.3
+
+
+
IndexedDB
+
Blob
+
Blob
+
Base-64
+
Blob
+
+
+
Blob
+
+
+
WebSQL
+
+
+
Blob
+
Blob
+
UTF-16 Blob
+
Blob
+
+
+
+
+
+Attachments are deduplicated based on their MD5 sum, so duplicate attachments won't take up extra space.
+
+To truly remove an attachment from the data store, you will need to use [compaction]({{ site.baseurl }}/api.html#compaction) to remove document revisions that reference that attachment.
+
+{% include anchor.html class="h3" title="Is it safe to upgrade PouchDB?" hash="safe_to_upgrade" %}
+
+Since v1.0.0, PouchDB has supported automatic schema migrations. This means that if you open a database that was built with an older version of PouchDB, the newer version will run all the steps necessary to get the old database up to date. We have extensive tests in place to guarantee that this feature works correctly.
+
+Even in the case of a major version release, PouchDB still performs the schema migrations. So for instance, you can create a database with PouchDB 1.0.0 and it will still work after you open it in 3.0.0.
+
+Once a database is migrated, however, you can no longer open it with an older version of PouchDB. So if an update contains a migration, it will be clearly marked in the release notes.
+
+{% include anchor.html class="h3" title="How is PouchDB different from CouchDB?" hash="couchdb_differences" %}
+
+PouchDB is also a CouchDB client, and you should be able to switch between a local database or an online CouchDB instance without changing any of your application's code.
+
+However, there are some minor differences to note:
+
+**View Collation** - CouchDB uses ICU to order keys in a view query; in PouchDB they are ASCII ordered.
+
+**View Offset** - CouchDB returns an `offset` property in the view results. In PouchDB, `offset` just mirrors the `skip` parameter rather than returning a true offset.
diff --git a/docs/version/8.0.0/getting-started.md b/docs/version/8.0.0/getting-started.md
new file mode 100644
index 0000000000..ea42b4df88
--- /dev/null
+++ b/docs/version/8.0.0/getting-started.md
@@ -0,0 +1,215 @@
+---
+layout: 2ColLeft.html
+title: Getting Started Guide
+sidebar: ./nav.html
+---
+
+{% include alert/start.html variant="info" %}
+
+If you get stuck, download a working version,
+or check out the full repo
+and commit history.
+
+{% include alert/end.html %}
+
+In this tutorial we will write a basic Todo web application based on [TodoMVC](http://todomvc.com/) that syncs to an online CouchDB server. It should take around 10 minutes.
+
+{% include anchor.html class="h3" title="Video Tutorial" hash="video_tutorial" %}
+
+Prefer video tutorials? This guide is available in video format:
+
+{% include iframe.html src="https://www.youtube.com/embed/-Z7UF2TuSp0" %}
+
+{% include anchor.html class="h3" title="Download Assets" hash="download" %}
+
+We will start with a template of the project where all the data related functions have been replaced with empty stubs. Download and unzip [pouchdb-getting-started-todo.zip](https://github.com/pouchdb/pouchdb-getting-started-todo/archive/master.zip). When dealing with XHR and IndexedDB you are better off running web pages from a server as opposed to a filesystem. To do this you can run:
+
+{% highlight bash %}
+$ cd pouchdb-getting-started-todo
+$ python -m SimpleHTTPServer # for Python 2
+$ python -m http.server # for Python 3
+{% endhighlight %}
+
+Then visit [http://127.0.0.1:8000/](http://127.0.0.1:8000/). If you see the following screenshot, you are good to go:
+
+{% include img.html src="screenshots/todo-1.png" alt="Todo Screenshot" %}
+
+It's also a good idea to open your browser's console so you can see any errors or confirmation messages.
+
+{% include anchor.html class="h3" title="Installing PouchDB" hash="installing_pouchdb" %}
+
+Open `index.html` and include PouchDB in the app by adding a script tag:
+
+{% highlight html %}
+
+
+
+{% endhighlight %}
+
+PouchDB is now installed in your app and ready to use! (In production, you should use a local copy of the script.)
+
+{% include anchor.html class="h3" title="Creating a database" hash="creating_a_database" %}
+
+The rest of the work will be done inside `app.js`. We will start by creating a database to enter your todos. To create a database simply instantiate a new PouchDB object with the name of the database:
+
+{% highlight js %}
+// EDITING STARTS HERE (you don't need to edit anything above this line)
+
+const db = new PouchDB('todos');
+const remoteCouch = false;
+{% endhighlight %}
+
+You don't need to create a schema for the database. After giving it a name, you can immediately start writing objects to it.
+
+{% include anchor.html class="h3" title="Write todos to the database" hash="write_todos_to_database" %}
+
+The first thing we shall do is start writing items to the database. The main input will call `addTodo` with the current text when the user presses `Enter`. We can complete this function with the following code:
+
+{% highlight js %}
+function addTodo(text) {
+ const todo = {
+ _id: new Date().toISOString(),
+ title: text,
+ completed: false
+ };
+ db.put(todo, function callback(err, result) {
+ if (!err) {
+ console.log('Successfully posted a todo!');
+ }
+ });
+}
+{% endhighlight %}
+
+In PouchDB each document is required to have a unique `_id`. Any subsequent writes to a document with the same `_id` will be considered updates. Here we are using a date string as an `_id`. For our use case, it will be unique, and it can also be used to sort items in the database. You can use `db.post()` if you want random ids. The `_id` is the only thing required when creating a new document. The rest of the object you can create as you like.
+
+The `callback` function will be called once the document has been written (or failed to write). If the `err` argument is not null, then it will have an object explaining the error, otherwise the `result` will hold the result.
+
+{% include anchor.html class="h3" title="Show items from the database" hash="show_database_items" %}
+
+We have included a helper function `redrawTodosUI` that takes an array of todos to display, so all we need to do is read the todos from the database. Here we will simply read all the documents using `db.allDocs`. The `include_docs` option tells PouchDB to give us the data within each document, and the `descending` option tells PouchDB how to order the results based on their `_id` field, giving us newest first.
+
+{% highlight js %}
+function showTodos() {
+ db.allDocs({include_docs: true, descending: true}, function(err, doc) {
+ redrawTodosUI(doc.rows);
+ });
+}
+{% endhighlight %}
+
+Once you have included this code, you should be able to refresh the page to see any todos you have entered.
+
+{% include anchor.html class="h3" title="Update the UI" hash="update_the_ui" %}
+
+We don't want to refresh the page to see new items. More typically you would update the UI manually when you write data to it, however, in PouchDB you may be syncing data remotely, so you want to make sure you update whenever the remote data changes. To do this we will call `db.changes` which subscribes to updates to the database, wherever they come from. You can enter this code between the `remoteCouch` and `addTodo` declaration:
+
+{% highlight js %}
+const remoteCouch = false;
+
+db.changes({
+ since: 'now',
+ live: true
+}).on('change', showTodos);
+
+// We have to create a new todo document and enter it in the database
+function addTodo(text) {
+{% endhighlight %}
+
+So every time an update happens to the database, we redraw the UI to show the new data. The `live` flag means this function will continue to run indefinitely. Now try entering a new todo and it should appear immediately.
+
+{% include anchor.html class="h3" title="Edit a todo" hash="edit_a_todo" %}
+
+When the user checks a checkbox, the `checkboxChanged` function will be called, so we'll fill in the code to edit the object and call `db.put`:
+
+{% highlight js %}
+function checkboxChanged(todo, event) {
+ todo.completed = event.target.checked;
+ db.put(todo);
+}
+{% endhighlight %}
+
+This is similar to creating a document, however the document must also contain a `_rev` field (in addition to `_id`), otherwise the write will be rejected. This ensures that you don't accidentally overwrite changes to a document.
+
+You can test that this works by checking a todo item and refreshing the page. It should stay checked.
+
+{% include anchor.html class="h3" title="Delete an object" hash="delete_an_object" %}
+
+To delete an object you can call db.remove with the object.
+
+{% highlight js %}
+function deleteButtonPressed(todo) {
+ db.remove(todo);
+}
+{% endhighlight %}
+
+Similar to editing a document, both the `_id` and `_rev` properties are required. You may notice that we are passing around the full object that we previously read from the database. You can of course manually construct the object, like: `{_id: todo._id, _rev: todo._rev}`, but passing around the existing object is usually more convenient and less error prone.
+
+{% include anchor.html class="h3" title="Complete rest of the Todo UI" hash="complete_todo_ui" %}
+
+`todoBlurred` is called when the user edits a document. Here we'll delete the document if the user has entered a blank title, and we'll update it otherwise.
+
+{% highlight js %}
+function todoBlurred(todo, event) {
+ const trimmedText = event.target.value.trim();
+ if (!trimmedText) {
+ db.remove(todo);
+ } else {
+ todo.title = trimmedText;
+ db.put(todo);
+ }
+}
+{% endhighlight %}
+
+{% include anchor.html class="h3" title="Installing CouchDB" hash="installing_couchdb" %}
+
+Now we'll implement the syncing. You need to have a compatible server instance. You can install either [PouchDB-Server](https://github.com/pouchdb/pouchdb-server), [CouchDB](http://couchdb.apache.org/) or use an hosted Couch service such as [Cloudant](https://cloudant.com/)
+
+{% include anchor.html class="h3" title="Enabling CORS" hash="enabling_cors" %}
+
+To replicate directly with CouchDB, you need to make sure CORS is enabled. Only set the username and password if you have set them previously. By default, CouchDB will be installed in "Admin Party," where username and password are not needed. You will need to replace `myname.example.com` with your own host (`127.0.0.1:5984` if installed locally):
+
+You can enable CORS in CouchDB using `curl` or the Futon web interface, but we've saved you some time by making a Node script called [add-cors-to-couchdb](https://github.com/pouchdb/add-cors-to-couchdb). Just run:
+
+{% highlight bash %}
+$ npm install -g add-cors-to-couchdb
+$ add-cors-to-couchdb
+{% endhighlight %}
+
+Or if your database is not at `127.0.0.1:5984`:
+
+{% highlight bash %}
+$ add-cors-to-couchdb http://me.example.com -u myusername -p mypassword
+{% endhighlight %}
+
+You can check that CORS is now enabled by visiting [http://localhost:5984/_utils/config.html](http://localhost:5984/_utils/config.html) in your browser. You should see something like this:
+
+{% include img.html src="cors_in_couchdb.png" alt="CORS settings in CouchDB" %}
+
+{% include anchor.html class="h3" title="Implement basic two way sync" hash="basic_two_way_sync" %}
+
+Now we will have the todo list sync. Back in `app.js` we need to specify the address of the remote database. Remember to replace `user`, `pass` and `myname.example.com` with the credentials of your own CouchDB instance:
+
+{% highlight js %}
+// EDITING STARTS HERE (you don't need to edit anything above this line)
+
+const db = new PouchDB('todos');
+const remoteCouch = 'http://user:pass@myname.example.com/todos';
+{% endhighlight %}
+
+Then we can implement the sync function like so:
+
+{% highlight js %}
+function sync() {
+ syncDom.setAttribute('data-sync-state', 'syncing');
+ const opts = {live: true};
+ db.replicate.to(remoteCouch, opts, syncError);
+ db.replicate.from(remoteCouch, opts, syncError);
+}
+{% endhighlight %}
+
+`db.replicate()` tells PouchDB to transfer all the documents `to` or `from` the `remoteCouch`. This can either be a string identifier or a PouchDB object. We call this twice: once to receive remote updates, and once to push local changes. Again, the `live` flag is used to tell PouchDB to carry on doing this indefinitely. The callback will be called whenever this finishes. For live replication, this will mean an error has occurred, like losing your connection or you canceled the replication.
+
+You should be able to open [the todo app](http://127.0.0.1:8000) in another browser and see that the two lists stay in sync with any changes you make to them. You may also want to look at your CouchDB's Futon administration page and see the populated database.
+
+{% include anchor.html class="h3" title="Congratulations!" hash="congratulations" %}
+
+You've completed your first PouchDB application. This is a basic example, and a real world application will need to integrate more error checking, user signup, etc. But you should now understand the basics you need to start working on your own PouchDB project. If you have any more questions, please get in touch on [IRC](ircs://irc.libera.chat:6697#pouchdb) [(web)](https://web.libera.chat/#pouchdb) or the [mailing list](https://groups.google.com/forum/#!forum/pouchdb).
diff --git a/docs/version/8.0.0/guides/async-code.md b/docs/version/8.0.0/guides/async-code.md
new file mode 100644
index 0000000000..9f087c88c2
--- /dev/null
+++ b/docs/version/8.0.0/guides/async-code.md
@@ -0,0 +1,211 @@
+---
+index: 6
+layout: guide.html
+title: Asynchronous code
+sidebar: guides_nav.html
+---
+
+PouchDB provides a fully **asynchronous** API. This ensures that when you talk to PouchDB, the UI doesn't stutter, because the DOM is not being blocked by database operations.
+
+However, working with asynchronous code can be very complex, especially if you're only accustomed to synchronous APIs. So it's worth going over some of the basics.
+
+{% include anchor.html title="I promise to call you back..." hash="i-promise-to-call-you-back" %}
+
+To make things as flexible as possible for PouchDB users, the API is provided in both **callback** format and **promise** format.
+
+The **callback** format looks like this:
+
+```js
+db.get('mittens', function (error, doc) {
+ if (error) {
+ // oh noes! we got an error
+ } else {
+ // okay, doc contains our document
+ }
+});
+```
+
+The **promise** format looks like this:
+
+```js
+db.get('mittens').then(function (doc) {
+ // okay, doc contains our document
+}).catch(function (err) {
+ // oh noes! we got an error
+});
+```
+
+Basically, if you include a callback as the last argument in a function, then PouchDB assumes you want the callback style. Otherwise it assumes you want the promise style.
+
+{% include anchor.html title="Let's talk about promises" hash="lets-talk-about-promises" %}
+
+For this guide, we will use the **promise** format for a few reasons:
+
+1. Callbacks easily lead to spaghetti code, or to the [pyramid of doom](https://medium.com/@wavded/managing-node-js-callback-hell-1fe03ba8baf).
+2. Promises generally lead to better code organization, although they do have a steep learning curve.
+
+If you already understand promises, you can [skip to the next section](updating-deleting.html).
+
+{% include alert/start.html variant="info"%}
+{% markdown %}
+
+**What about async/await?** Async functions are an experimental ES7 syntax that enhances promise-based APIs by adding
+the `async` and `await` keywords. For more information about `async`/`await`, read [our introductory blog post]({{ site.baseurl }}/2015/03/05/taming-the-async-beast-with-es7.html).
+
+{% endmarkdown %}
+{% include alert/end.html%}
+
+{% include anchor.html title="Understanding promises" hash="understanding-promises" %}
+
+If you have the time, you are strongly encouraged to watch [this 50-minute video: "Redemption from Callback Hell"](http://youtu.be/hf1T_AONQJU). The rest of this chapter basically summarizes that video.
+
+The best way to think of promises is that they bring keywords like `return` and `try/catch` to asynchronous code.
+
+Synchronous code:
+
+```js
+function returnSomething() {
+ try {
+ doSomething();
+ doSomethingElse();
+ return true;
+ } catch (err) {
+ console.log(err);
+ }
+}
+```
+
+Asynchronous code:
+
+```js
+function returnSomething() {
+ return doSomething().then(function () {
+ return doSomethingElse();
+ }).then(function () {
+ return true;
+ }).catch(function (err) {
+ console.log(err);
+ });
+}
+```
+
+{% include anchor.html title="Use `catch()` to catch errors" hash="use-catch-to catch errors" %}
+
+The big advantage of working with Promises in asynchronous code is that you can always attach a `catch` function to the end of a big promise chain, and any errors that occur along the way will show up at the end.
+
+This avoids endless `if (err) {}` checking in the callback world:
+
+```js
+doSomething(function (err, result) {
+ if (err) {
+ // handle error
+ }
+ doSomethingElse(function (err, result) {
+ if (err) {
+ // handle error again...
+ }
+ doSomethingYetAgain(function (err, result) {
+ if (err) {
+ // seriously? okay, handle error again...
+ }
+ });
+ });
+});
+```
+
+Instead, in the promise world, you can have a long chain of asynchronous operations with a single `catch` at the end. To use PouchDB as an example:
+
+```js
+db.put({_id: 'charlie', age: 21}).then(function () {
+ return db.get('charlie');
+}).then(function (charlie) {
+ // increment Charlie's age
+ charlie.age++;
+ return db.put(charlie);
+}).then(function () {
+ return db.get('charlie');
+}).then(function (charlie) {
+ // increment Charlie's age again
+ charlie.age++;
+ return db.put(charlie);
+}).then(function () {
+ return db.get('charlie');
+}).then(function (charlie) {
+ console.log(charlie);
+}).catch(function (err) {
+ console.log(err);
+});
+```
+
+You should see:
+
+```js
+{"age":23,"_id":"charlie","_rev":"3-e794618b4e39ed566cc68b56f5426e8e"}
+```
+
+You can see **[a live example](http://bl.ocks.org/nolanlawson/612f95cbbb69eaafc2d5)** of this code.
+
+In this example, we put/get a document 3 times in a row. At the very end, there is a `catch()` statement to catch any errors along the way.
+
+What kind of errors might we run into? Well, let's imagine that we accidentally misspell the id `'charlie'` at some point. In this case, we will gracefully catch the error. Here's another **[live example](http://bl.ocks.org/nolanlawson/0f1c815cb5fe74cff5fc)**.
+
+You should see:
+
+```js
+{"status":404,"name":"not_found","message":"missing"}
+```
+
+This is really nice! No matter where the misspelling is, the error can be handled within a single function. That's much nicer than having to do `if (err){}` an endless number of times!
+
+{% include anchor.html title="An alternate way of catching errors" hash="an-alternate-way-of-catching-errors" %}
+
+If you've been doing promises for awhile, you might have seen this instead:
+
+```js
+db.get('charlie').then(function (charlie) {
+ // we got the charlie doc
+}, function (err) {
+ // we got an error
+})
+```
+
+This is equivalent to:
+
+```js
+db.get('charlie').then(function (charlie) {
+ // we got the charlie doc
+}).catch(function (err) {
+ // we got an error
+})
+```
+
+The `catch()` method is just syntactic sugar. You can use either format.
+
+{% include anchor.html title="Promises 101" hash="promises-101" %}
+
+The `then()` method takes a function. What can you do within this function? Three things:
+
+* Return another promise
+* Throw an error
+* Return a non-promise object (or `undefined`)
+
+Another way to think of it is this:
+
+```js
+db.get('charlie').then(function (charlie) {
+ // Within this function, you can do
+ // try/catch/return like you normally would,
+ // and it will be handled asynchronously!
+}).then(function (result) {
+ // If the previous function returned something
+ // (or returned undefined), it will show up here
+ // as "result".
+}).catch(function (err) {
+ // If the previous function threw an error,
+ // it will show up here as "err".
+});
+```
+
+{% include anchor.html title="Next" hash="next" %}
+
+Now that you have a grasp on promises, let's learn about updating and deleting documents.
diff --git a/docs/version/8.0.0/guides/attachments.md b/docs/version/8.0.0/guides/attachments.md
new file mode 100644
index 0000000000..37c0fc37c7
--- /dev/null
+++ b/docs/version/8.0.0/guides/attachments.md
@@ -0,0 +1,314 @@
+---
+index: 9
+layout: guide.html
+title: Working with attachments
+sidebar: guides_nav.html
+---
+
+Attachments are where PouchDB can get really fun.
+
+The big difference between storage engines like WebSQL/IndexedDB and the older localStorage API is that you can stuff [a lot more data](https://web.dev/storage-for-the-web/) in it.
+
+PouchDB attachments allow you to use that to full advantage to store images, MP3s, zip files, or whatever you want.
+
+{% include anchor.html title="How attachments are stored" hash="how-attachments-are-stored" %}
+
+As their name implies, attachments are *attached* to documents. You can work with attachments either in base64-encoded format, or as a [Blob](https://developer.mozilla.org/en-US/docs/Web/API/Blob).
+
+For example, here is a very simple document with a plain text attachment, stored as base64.
+
+```js
+db.put({
+ _id: 'mydoc',
+ _attachments: {
+ 'myattachment.txt': {
+ content_type: 'text/plain',
+ data: 'aGVsbG8gd29ybGQ='
+ }
+ }
+});
+```
+
+Our document has the usual `_id` field, but it also has a special `_attachments` field that holds the attachments. Documents can have as many attachments as you want.
+
+{% include alert/start.html variant="info" %}
+
+When you create an attachment, you need to specify its content_type, otherwise known as the MIME type. Common MIME types include 'text/plain' for plain text, 'image/png' for PNG images, and 'image/jpeg' for JPG images.
+
+{% include alert/end.html %}
+
+As it turns out, `'aGVsbG8gd29ybGQ='` is just the string `'hello world'` encoded in base64. You can use the `atob()` and `btoa()` methods in your browser to verify.
+
+```js
+btoa('hello world') // "aGVsbG8gd29ybGQ="
+atob('aGVsbG8gd29ybGQ=') // "hello world"
+```
+
+Let's see what happens after we store this document. If you try to `get()` it normally, you may be surprised to see that the attachment data itself isn't returned:
+
+```js
+db.get('mydoc').then(function (doc) {
+ console.log(doc);
+});
+```
+
+The returned document will look like this:
+
+```js
+{
+ "_attachments": {
+ "myattachment.txt": {
+ "content_type": "text/plain",
+ "digest": "md5-XrY7u+Ae7tCTyyK7j1rNww==",
+ "stub": true
+ }
+ },
+ "_id": "mydoc",
+ "_rev": "1-e8a84187bb4e671f27ec11bdf7320aaa"
+}
+```
+
+You can see **[a live example](http://bl.ocks.org/nolanlawson/0a4b1267d3a5b5edd7b1)** of this code.
+
+By default, PouchDB will only give you an attachment **stub**, which contains a `digest`, i.e. the [md5sum](http://en.wikipedia.org/wiki/Md5sum) of the binary attachment.
+
+To get the full attachments when using `get()` or `allDocs()`, you need to specify `{attachments: true}`:
+
+```js
+db.get('mydoc', {attachments: true}).then(function (doc) {
+ console.log(doc);
+});
+```
+
+Then you'll get back the full attachment, base64-encoded:
+
+```js
+{
+ "_attachments": {
+ "myattachment.txt": {
+ "content_type": "text/plain",
+ "digest": "md5-XrY7u+Ae7tCTyyK7j1rNww==",
+ "data": "aGVsbG8gd29ybGQ="
+ }
+ },
+ "_id": "mydoc",
+ "_rev": "1-e8a84187bb4e671f27ec11bdf7320aaa"
+}
+```
+
+You can see **[a live example](http://bl.ocks.org/nolanlawson/b6d6164035f1fa0d38a8)** of this code.
+
+{% include anchor.html title="Image attachments" hash="image-attachments" %}
+
+Plaintext is cool and all, but you know what would be *really* awesome? Storing images.
+
+So let's do it! In this example, we'll put a document with a small icon attachment, represented as a base64-encoded string. Then we'll fetch it and display the icon as a normal `` tag:
+
+```js
+db.put({
+ _id: 'meowth',
+ _attachments: {
+ 'meowth.png': {
+ content_type: 'image/png',
+ data: 'iVBORw0KGgoAAAANSUhEUgAAACgAAAAkCAIAAAB0Xu9BAAAABGdBTUEAALGPC/xhBQAAAuNJREFUWEetmD1WHDEQhDdxRMYlnBFyBIccgdQhKVcgJeQMpE5JSTd2uqnvIGpVUqmm9TPrffD0eLMzUn+qVnXPwiFd/PP6eLh47v7EaazbmxsOxjhTT88z9hV7GoNF1cUCvN7TTPv/gf/+uQPm862MWTL6fff4HfDx4S79/oVAlAUwqOmYR0rnazuFnhfOy/ErMKkcBFOr1vOjUi2MFn4nuMil6OPh5eGANLhW3y6u3aH7ijEDCxgCvzFmimvc95TekZLyMSeJC68Bkw0kqUy1K87FlpGZqsGFCyqEtQNDdFUtFctTiuhnPKNysid/WFEFLE2O102XJdEE+8IgeuGsjeJyGHm/xHvQ3JtKVsGGp85g9rK6xMHtvHO9+WACYjk5vkVM6XQ6OZubCJvTfPicYPeHO2AKFl5NuF5UK1VDUbeLxh2BcRGKTQE3irHm3+vPj6cfCod50Eqv5QxtwBQUGhZhbrGVuRia1B4MNp6edwBxld2sl1splfHCwfsvCZfrCQyWmX10djjOlWJSSy3VQlS6LmfrgNvaieRWx1LZ6s9co+P0DLsy3OdLU3lWRclQsVcHJBcUQ0k9/WVVrmpRzYQzpgAdQcAXxZzUnFX3proannrYH+Vq6KkLi+UkarH09mC8YPr2RMWOlEqFkQClsykGEv7CqCUbXcG8+SaGvJ4a8d4y6epND+pEhxoN0vWUu5ntXlFb5/JT7JfJJqoTdy9u9qc7ax3xJRHqJLADWEl23cFWl4K9fvoaCJ2BHpmJ3s3z+O0U/DmzdMjB9alWZtg4e3yxzPa7lUR7nkvxLHO9+tvJX3mtSDpwX8GajB283I8R8a7D2MhUZr1iNWdny256yYLd52DwRYBtRMvE7rsmtxIUE+zLKQCDO4jlxB6CZ8M17GhuY+XTE8vNhQiIiSE82ZsGwk1pht4ZSpT0YVpon6EvevOXXH8JxVR78QzNuamupW/7UB7wO/+7sG5V4ekXb4cL5Lyv+4IAAAAASUVORK5CYII='
+ }
+ }
+}).then(function () {
+ return db.getAttachment('meowth', 'meowth.png');
+}).then(function (blob) {
+ const url = URL.createObjectURL(blob);
+ const img = document.createElement('img');
+ img.src = url;
+ document.body.appendChild(img);
+}).catch(function (err) {
+ console.log(err);
+});
+```
+
+You can see **[a live example](http://bl.ocks.org/nolanlawson/2a5f98a66c9fe3ae3532)** of this code.
+
+You should be unsurprised to see a cat smiling back at you. If the kitten theme bothers you, then you haven't been on the Internet very long.
+
+How does this code work? First off, we are making use of the `URL.createObjectURL()` method, which is a standard HTML5 method that converts a `Blob` to a URL that we can easily use as the `src` of an `img`.
+
+Second off, we are using the `getAttachment()` API, which returns a `Blob` rather than a base64-encoded string. To be clear: we can always convert between base64 and `Blob`s, but in this case, `getAttachment()` is just more convenient.
+
+
+{% include anchor.html title="Directly storing binary data" hash="directly-storing-binary-data" %}
+
+Up to now, we've been supplying our attachments as base64-encoded strings. But we can also create the Blobs ourselves and store those directly in PouchDB.
+
+Another shortcut we can use is the `putAttachment()` API, which simply modifies the existing document to hold a new attachment. Or, if the document does not exist, it will create an empty one.
+
+{% include alert/start.html variant="info" %}
+
+In Node.js, PouchDB uses Buffers instead of Blobs. Otherwise, the same rules apply.
+
+{% include alert/end.html %}
+
+For instance, we can read the image data from an `` tag using a `canvas` element, and then directly write that Blob to PouchDB:
+
+```js
+function convertImgToBlob(img, callback) {
+ const canvas = document.createElement('canvas');
+ const context = canvas.getContext('2d');
+ context.drawImage(img, 0, 0);
+
+ // Warning: toBlob() isn't supported by every browser.
+ // You may want to use blob-util.
+ canvas.toBlob(callback, 'image/png');
+}
+
+const catImage = document.getElementById('cat');
+convertImgToBlob(catImage, function (blob) {
+ db.putAttachment('meowth', 'meowth.png', blob, 'image/png').then(function () {
+ return db.get('meowth', {attachments: true});
+ }).then(function (doc) {
+ console.log(doc);
+ });
+});
+```
+
+You can see **[a live example](http://bl.ocks.org/nolanlawson/edaf09b84185418a55d9)** of this code.
+
+This stores exactly the same image content as in the other example, which you can confirm by checking the base64-encoded output.
+
+{% include alert/start.html variant="warning" %}
+
+Blobs can be tricky to work with, especially when it comes to cross-browser support.
+You may find blob-util to be a useful
+addition to the attachment API. For instance, it has an
+imgSrcToBlob() method that will work cross-browser.
+
+{% include alert/end.html %}
+
+{% include anchor.html title="Allow the user to store an attachment" hash="allow-the-user-to-store-an-attachment" %}
+
+You can also upload a file with the HTML5 `File` API and store it directly in the database, because the data you get from the `` element is already a `Blob`.
+(See: [Blob API](https://developer.mozilla.org/en-US/docs/Web/API/Blob) and [File API](https://developer.mozilla.org/en-US/docs/Web/API/File), which inherits properties from the `Blob` Interface.)
+
+Here is an example of allowing a user to choose a file from their filesystem:
+
+```html
+
+```
+
+And then "uploading" that file directly into PouchDB:
+
+```js
+const input = document.querySelector('input');
+input.addEventListener('change', function () {
+ const file = input.files[0]; // file is a Blob
+
+ db.put({
+ _id: 'mydoc',
+ _attachments: {
+ filename: {
+ content_type: file.type,
+ data: file
+ }
+ }
+ }).catch(function (err) {
+ console.log(err);
+ });
+});
+```
+
+You can see **[a live example](http://bl.ocks.org/ntwcklng/f57b03c15e91c25e2cb5)** of this code.
+
+Select a file and you will see the stored file, `size`, and `type`, which are valid `Blob` properties. If you choose an image, it will also show the image!
+
+{% include anchor.html title="Base64 vs Blobs/Buffers" hash="base64-vs-blobs-buffers" %}
+
+Whether you supply attachments as base64-encoded strings or as Blobs/Buffers, PouchDB will try to store them in [the most efficient way](/faq.html#data_types).
+
+So when you insert your attachments, either format is acceptable. For instance, you can put Blobs/Buffers using `put()`:
+
+```js
+db.put({
+ _id: 'mydoc',
+ _attachments: {
+ 'myattachment.txt': {
+ content_type: 'text/plain',
+ data: myBlob
+ }
+ }
+});
+```
+
+And you can also pass base64-encoded strings to `putAttachment()`:
+
+```js
+db.putAttachment('mydoc', 'myattachment.png', myBase64String, 'image/png');
+```
+
+You can also insert multiple attachments at once using `put()`:
+
+```js
+db.put({
+ _id: 'mydoc',
+ _attachments: {
+ 'myattachment1.txt': {
+ content_type: 'text/plain',
+ data: myBlob1
+ },
+ 'myattachment2.txt': {
+ content_type: 'text/plain',
+ data: myBlob2
+ },
+ 'myattachment3.txt': {
+ content_type: 'text/plain',
+ data: myBlob3
+ },
+ // etc.
+ }
+});
+```
+
+The `bulkDocs()` and `post()` APIs also accept attachments in either format.
+
+When you fetch attachments, however, `getAttachment()` will always return Blobs/Buffers.
+
+The other "read" APIs, such as `get()`, `allDocs()`, `changes()`, and `query()` have an `{attachments: true}` option that returns the attachments base64-encoded strings. If you add `{binary: true}`, though, they will return Blobs/Buffers.
+
+{% include alert/start.html variant="info" %}
+{% markdown %}
+
+**Performance tip:** If you can insert and retrieve your attachments using _only_ Blobs/Buffers, then you will typically get better performance, especially when it comes to memory usage. The base64 string format is mostly provided for developer convenience and debugging.
+
+{% endmarkdown %}
+{% include alert/end.html %}
+
+
+{% include anchor.html title="Blob types" hash="blob-types" %}
+
+Blobs have their own `type`, but there is also a `content_type` that you specify when you store it in PouchDB:
+
+```js
+const myBlob = new Blob(['I am plain text!'], {type: 'text/plain'});
+console.log(myBlob.type); // 'text/plain'
+
+db.put({
+ _id: 'mydoc',
+ _attachments: {
+ 'myattachment.txt': {
+ content_type: 'text/plain',
+ data: myBlob
+ }
+ }
+});
+```
+
+The reason for this redundancy is 1) Buffers in Node do not have a `type`, and 2) the CouchDB attachment format requires it.
+
+So for best results, you should ensure that your Blobs have the same type as the one reported to PouchDB. Otherwise you may see inconsistent behavior (e.g. in IndexedDB, where the Blob is stored as-is on compatible browsers).
+
+{% include anchor.html title="Related API documentation" hash="related-api-documentation" %}
+
+* [putAttachment()](/api.html#save_attachment)
+* [getAttachment()](/api.html#get_attachment)
+* [removeAttachment()](/api.html#delete_attachment)
+
+{% include anchor.html title="Next" hash="next" %}
+
+Now that you can attach cat pictures to all your documents (and why wouldn't you?), let's talk about replication.
diff --git a/docs/version/8.0.0/guides/bulk-operations.md b/docs/version/8.0.0/guides/bulk-operations.md
new file mode 100644
index 0000000000..28ae8634fe
--- /dev/null
+++ b/docs/version/8.0.0/guides/bulk-operations.md
@@ -0,0 +1,132 @@
+---
+index: 8
+layout: guide.html
+title: Bulk operations
+sidebar: guides_nav.html
+---
+
+You can `get()`, `put()`, and `remove()` single documents to your heart's content, but a database isn't a database unless it can handle many operations at once!
+
+PouchDB provides two methods for bulk operations - `bulkDocs()` for bulk writes, and `allDocs()` for bulk reads.
+
+{% include anchor.html title="Use `bulkDocs()` to write many docs" hash="use-bulkdocs-to-write-many-docs" %}
+
+The `bulkDocs()` API is very simple. It just takes a list of documents that you want to `put()` into the database:
+
+```js
+db.bulkDocs([
+ {
+ _id: 'mittens',
+ occupation: 'kitten',
+ cuteness: 9.0
+ },
+ {
+ _id: 'katie',
+ occupation: 'kitten',
+ cuteness: 7.0
+ },
+ {
+ _id: 'felix',
+ occupation: 'kitten',
+ cuteness: 8.0
+ }
+]);
+```
+
+You can see **[a live example](http://bl.ocks.org/nolanlawson/038a45134341f3b7235b)** of this code.
+
+This code is equivalent to `put()`ing each document separately:
+
+```js
+db.put({
+ _id: 'mittens',
+ occupation: 'kitten',
+ cuteness: 9.0
+}).then(function () {
+ return db.put({
+ _id: 'katie',
+ occupation: 'kitten',
+ cuteness: 7.0
+ });
+}).then(function () {
+ return db.put({
+ _id: 'felix',
+ occupation: 'kitten',
+ cuteness: 8.0
+ });
+});
+```
+
+{% include anchor.html title="Why bulk up with `bulkDocs()`?" hash="why-bulk-up-with-bulkdocs" %}
+
+Bulk operations tend to be faster than individual operations, because they can be combined into a single transaction (in a local IndexedDB/WebSQL) or a single HTTP request (in a remote CouchDB).
+
+You can also update or delete multiple documents this way. You just need to include the `_rev` and `_deleted` values as previously discussed. The same rules as for `put()` apply to each individual document.
+
+{% include alert/start.html variant="warning" %}
+
+Neither bulkDocs() nor allDocs() constitutes a transaction in the traditional sense. That means that, if a single put() fails, you should not assume that the others will fail.
+
+
+By design, CouchDB and PouchDB do not support transactions. A document is the smallest unit of operations.
+{% include alert/end.html %}
+
+{% include anchor.html title="Use `allDocs()` to read many docs" hash="use-alldocs-to-read-many-docs" %}
+
+Likewise, `allDocs()` is a method that allows you to read many documents at once.
+
+Most crucially, when you read from `allDocs()`, the documents are returned *sorted by order of `_id`*. This makes the `_id` a very powerful field that you can use for more than just uniquely identifying your documents.
+
+For instance, if you refer back to [the live example](http://bl.ocks.org/nolanlawson/038a45134341f3b7235b) above, you'll notice that the kittens are sorted by their name, because their names are used as their `_id`s.
+
+Another common way to take advantage of this is to use `new Date().toJSON()` as your document `_id`s. In this way, all your documents will be sorted by date.
+
+For instance, let's save three kittens with three different dates, and then fetch them sorted by date:
+
+```js
+db.put({
+ _id: new Date().toJSON(),
+ name: 'Mittens',
+ occupation: 'kitten',
+ cuteness: 9.0
+}).then(function () {
+ return db.put({
+ _id: new Date().toJSON(),
+ name: 'Katie',
+ occupation: 'kitten',
+ cuteness: 7.0
+ });
+}).then(function () {
+ return db.put({
+ _id: new Date().toJSON(),
+ name: 'Felix',
+ occupation: 'kitten',
+ cuteness: 8.0
+ });
+}).then(function () {
+ return db.allDocs({include_docs: true});
+}).then(function (response) {
+ console.log(response);
+}).catch(function (err) {
+ console.log(err);
+});
+```
+
+You can see **[a live example](http://bl.ocks.org/nolanlawson/8f58dbc360348a4c95f6)** to confirm that the kittens are sorted by the order they were put into the database.
+
+{% include anchor.html title="Please use `allDocs()`. Seriously." hash="please-use-alldocs" %}
+
+`allDocs()` is the unsung star of the PouchDB world. It not only returns documents in order – it also allows you to reverse the order, filter by `_id`, slice and dice using "greater than" and "less than" operations on the `_id`, and much more.
+
+Far too many developers overlook this valuable API, because they misunderstand it. When a developer says "my PouchDB app is slow!", it is usually because they are using the slow `query()` API when they should be using the fast `allDocs()` API.
+
+For details on how to effectively use `allDocs()`, you are strongly recommended to read ["Pagination strategies with PouchDB"]({{ site.baseurl }}/2014/04/14/pagination-strategies-with-pouchdb.html). For 99% of your applications, you should be able to use `allDocs()` for all the pagination/sorting/searching functionality that you need.
+
+{% include anchor.html title="Related API documentation" hash="related-api-documentation" %}
+
+* [bulkDocs()](/api.html#batch_create)
+* [allDocs()](/api.html#batch_fetch)
+
+{% include anchor.html title="Next" hash="next" %}
+
+Now that you've fallen helplessly in love with `bulkDocs()` and `allDocs()`, let's turn our wandering gaze to attachments.
diff --git a/docs/version/8.0.0/guides/changes.md b/docs/version/8.0.0/guides/changes.md
new file mode 100644
index 0000000000..0bc3168016
--- /dev/null
+++ b/docs/version/8.0.0/guides/changes.md
@@ -0,0 +1,142 @@
+---
+index: 12
+layout: guide.html
+title: Changes feed
+sidebar: guides_nav.html
+---
+
+One of the brilliant things about CouchDB replication is that it makes it easy to learn about changes made to the database over time. CouchDB allows you to easily answer questions like:
+
+* What changes occurred to the database since a given time?
+* What changes occurred to this document?
+* What did this database look like a few days ago?
+
+For all of these and related questions, there's the `changes()` API.
+
+{% include anchor.html title="Basic changes usage" hash="basic-changes-usage" %}
+
+If you want to simply fetch all changes since the beginning of time, you can do:
+
+```js
+db.changes({
+ since: 0,
+ include_docs: true
+}).then(function (changes) {
+
+}).catch(function (err) {
+ // handle errors
+});
+```
+
+You can see a **[live example](http://bl.ocks.org/nolanlawson/7c32861af5d31a8fac4a)** of this code.
+
+Then you will have a list of all changes made to the database, in the order that they were made.
+
+One thing you will notice about the changes feed is that it actually omits non-leaf revisions to documents. For instance, in the live example, we skip from `seq` 1 immediately to `seq` 3.
+
+This is by design – the changes feed only tells us about leaf revisions. However, the order of those leaf revisions is determined by the order they were put in the database. So you may notice that `'firstDoc'` still appears before `'secondDoc'`, which appears before `'thirdDoc'`.
+
+Also notice the option `{include_docs: true}`. By default, the documents themselves are not included in the changes feed; only the `id`s, `rev`s, and whether or not they were `deleted`. With `{include_docs: true}`, however, each non-deleted change will have a `doc` property containing the new or modified document.
+
+{% include anchor.html title="Changes pagination" hash="changes-pagination" %}
+
+If you expect this to be a very large number of changes, you can also use the `limit` option to do pagination:
+
+```js
+const pageSize = 10;
+let lastSeq = 0;
+function fetchNextPage() {
+ return db.changes({
+ since: lastSeq,
+ limit: pageSize
+ }).then(function (changes) {
+ if (changes.results.length < pageSize) {
+ // done!
+ } else {
+ lastSeq = changes.last_seq;
+ return fetchNextPage();
+ }
+ });
+}
+
+fetchNextPage().catch(function (err) {
+ // handle errors
+});
+```
+
+You can see a **[live example](http://bl.ocks.org/nolanlawson/dcdeae555b31c2a6d332)** of this code.
+
+{% include anchor.html title="`seq` versus `_rev`" hash="seq-versus-rev" %}
+
+The changes feed lists each change with a corresponding `seq` integer. `seq` always starts with 0, and beyond that it increases monotonically. (In Cloudant, these are strings rather than integers.)
+
+`seq` can be thought of as a version number for the entire database. Basically it answers the question of "How many total changes have been made to all documents in this database?" This sets it apart from the revision hash `_rev`, which marks the changes made to a single document.
+
+However, the `seq` between two databases is not guaranteed to be kept in sync. CouchDB and PouchDB have slightly different ways of increasing their `seq` values, so really `seq` is only meaningful within a single database.
+
+{% include anchor.html title="Live changes feed" hash="live-changes-feed" %}
+
+Just like replication, you can also listen to a live changes feed. The way this works is very similar to the replication API:
+
+```js
+db.changes({
+ since: 'now'
+}).on('change', function (change) {
+ // received a change
+}).on('error', function (err) {
+ // handle errors
+});
+```
+
+In the above example, we've also taken advantage of the quasi-magical `'now'` option for `since`, which will give us all changes from the moment we start listening.
+
+This can be very useful for scenarios where you want to update the UI whenever something in the database changes, such as for a real-time chat application.
+
+{% include anchor.html title="Understanding changes" hash="understanding-changes" %}
+
+There are two types of changes:
+
+* Added or modified documents
+* Deleted documents
+
+To distinguish between the two types in a live `changes()` listener,
+you can use the following code:
+
+```js
+db.changes({
+ since: 'now',
+ live: true,
+ include_docs: true
+}).on('change', function (change) {
+ // change.id contains the doc id, change.doc contains the doc
+ if (change.deleted) {
+ // document was deleted
+ } else {
+ // document was added/modified
+ }
+}).on('error', function (err) {
+ // handle errors
+});
+```
+
+You can see a [live example](http://bl.ocks.org/nolanlawson/fa42662cdfeeaa7b78fc) of this code.
+
+Notice that `change.doc` contains the document (unless it's deleted), because we used `{include_docs: true}`.
+
+Also notice that new documents always have revisions starting with the string `'1-'`. Subsequent revisions start with `'2-'`, `'3-'`, `'4-'`, etc.
+
+{% include alert/start.html variant="info" %}
+
+
How can I distinguish between added and modified documents? Checking if the revision starts with '1-' is a pretty good trick. However, this will not work for databases that are replication targets, because replication only sends the latest versions of documents. This means that the '1-' revision may get skipped entirely, and the local database will only receive the 2nd, 3rd or 4th (etc.) revision. Conflicting revisions will also appear in the changes feed.
+
+
So the short answer is that you cannot. If you are trying to mirror changes in a non-Pouch structure (e.g. a list of DOM elements), then the best solution is to search all the DOM elements to see if the document already exists, or to re-run allDocs() for every change.
+
+{% include alert/end.html %}
+
+{% include anchor.html title="Related API documentation" hash="related-api-documentation" %}
+
+* [changes()](/api.html#changes)
+
+{% include anchor.html title="Next" hash="next" %}
+
+Now that we know how to hook our data spigot to the changes feed, let's look into using the very powerful `find()` API.
diff --git a/docs/version/8.0.0/guides/compact-and-destroy.md b/docs/version/8.0.0/guides/compact-and-destroy.md
new file mode 100644
index 0000000000..2c9ed1a9d2
--- /dev/null
+++ b/docs/version/8.0.0/guides/compact-and-destroy.md
@@ -0,0 +1,97 @@
+---
+index: 15
+layout: guide.html
+title: Compacting and destroying
+sidebar: guides_nav.html
+---
+
+By default, PouchDB and CouchDB are designed to store all document revisions forever. This is very similar to how Git works, and it helps ensure that two databases can consistently replicate with each other.
+
+However, if you allow your database to grow without bounds, it can end up taking up much more space than you need. This can especially be a problem in [browsers with storage quotas](/faq.html#data_limits).
+
+To mitigate this problem, PouchDB offers two recourses: compaction and destruction.
+
+{% include anchor.html title="Compacting a database" hash="compacting-a-database" %}
+
+When you compact a database, you tell PouchDB to optimize its current storage usage. CouchDB will do the same thing:
+
+```js
+return db.compact().then(function (info) {
+ // compaction complete
+}).catch(function (err) {
+ // handle errors
+});
+```
+
+From the API perspective, nothing should be different about the database after compaction, *except* that non-leaf revisions will no longer be available.
+
+```js
+db.put({_id: 'foo', version: 1}).then(function () {
+ return db.get('foo');
+}).then(function (doc) {
+ doc.version = 2;
+ return db.put(doc);
+}).then(function () { )
+ return db.compact();
+}).then(function () {
+ // DANGER!
+ // From now on, revision 1 is no longer available.
+}).catch(function (err) {
+ // handle errors
+});
+```
+
+You can see a **[live example](http://bl.ocks.org/nolanlawson/ff6eb521793e3a199864)** of this code.
+
+Compaction is a great feature, but it may not be what you desire if you want to retain a document's history from the beginning of time.
+
+However, if that's not a concern, then compaction is a harmless operation. In fact, since leaf revisions are retained, this means that you can still do [conflict resolution](/guides/conflicts.html) after compaction!
+
+{% include anchor.html title="Auto-compaction" hash="auto-compaction" %}
+
+If you really want to go all-in on compaction, then you can even put your database in `auto_compaction` mode. This means that it will automatically perform a `compact()` operation after every write.
+
+```js
+const db = new PouchDB('mydb', {auto_compaction: true});
+db.put({_id: 'foo', version: 1}).then(function () {
+ return db.get('foo');
+}).then(function (doc) {
+ doc.version = 2;
+ return db.put(doc);
+}).then(function () {
+ // Revision 1 is already unavailable!
+}).catch(function (err) {
+ // handle errors
+});
+```
+
+You can see a **[live example](http://bl.ocks.org/nolanlawson/b88f46d7cbaef8d93cba)** of this code.
+
+This feature is only available in local databases, not remote ones. On remote databases, the `auto_compaction` option will do nothing.
+
+{% include anchor.html title="Destroying a database" hash="destroying-a–database" %}
+
+We all love our databases, but sometimes good things must come to an end, and you need to snub out a database completely.
+
+So if you want to give your database to a nice farm family upstate, then the `destroy()` API is for you. It's very simple:
+
+```js
+new PouchDB('mydb').destroy().then(function () {
+ // database destroyed
+}).catch(function (err) {
+ // error occurred
+})
+```
+
+Note that destroying a database does not mean that replicated databases will also be destroyed. Destruction has nothing to do with the normal `put()`/`remove()` operations on documents, so it has no impact on replication.
+
+Also note that in Web SQL, the database will not really be destroyed – it will just have its tables dropped. This is because Web SQL does not support true database deletion.
+
+{% include anchor.html title="Related API documentation" hash="related-api-documentation" %}
+
+* [compact()](/api.html#compaction)
+* [destroy()](/api.html#delete_database)
+
+{% include anchor.html title="Next" hash="next" %}
+
+To wrap up, let's look at a special class of documents in PouchDB – local docs.
diff --git a/docs/version/8.0.0/guides/conflicts.md b/docs/version/8.0.0/guides/conflicts.md
new file mode 100644
index 0000000000..60898fe779
--- /dev/null
+++ b/docs/version/8.0.0/guides/conflicts.md
@@ -0,0 +1,160 @@
+---
+index: 11
+layout: guide.html
+title: Conflicts
+sidebar: guides_nav.html
+---
+
+Conflicts are an unavoidable reality when dealing with distributed systems. And make no mistake: client-server *is* a distributed system.
+
+CouchDB and PouchDB differ from many other sync solutions, because they bring the issue of conflicts front-and-center. With PouchDB, conflict resolution is entirely under your control.
+
+{% include alert/start.html variant="info" %}
+
+PouchDB exactly implements CouchDB's replication algorithm, so conflict resolution works the same in both. For the purposes of this article, "CouchDB" and "PouchDB" may be used interchangeably.
+
+{% include alert/end.html %}
+
+{% include anchor.html title="Two types of conflicts" hash="two-types-of-conflicts" %}
+
+In CouchDB, conflicts can occur in two places: immediately, when you try to commit a new revision, or later, when two peers have committed changes to the same document. Let's call these **immediate conflicts** and **eventual conflicts**.
+
+### Immediate conflicts
+
+**Immediate conflicts** can occur with any API that takes a `rev` or a document with `_rev` as input – `put()`, `post()`, `remove()`, `bulkDocs()`, and `putAttachment()`. They manifest as a `409` (conflict) error:
+
+```js
+const myDoc = {
+ _id: 'someid',
+ _rev: '1-somerev'
+};
+db.put(myDoc).then(function () {
+ // success
+}).catch(function (err) {
+ if (err.name === 'conflict') {
+ // conflict!
+ } else {
+ // some other error
+ }
+});
+```
+
+In your code, *you should always be handling conflicts*. No matter how unlikely it may seem, 409s can and do occur.
+
+For instance, if you are doing live replication, a document may be modified by somebody else while the user is working on it. If the remote changes are replicated to the local database before the user tries to commit their changes, then they will receive the above 409 error.
+
+#### Upsert
+
+In many cases, the most practical solution to the 409 problem is to retry the `put()` until it succeeds. If the user's intended change can be expressed as a **delta** (i.e. a change that doesn't depend on the current revision), then this is very easy to achieve.
+
+Borrowing a phrase from traditional databases, let's call this an **upsert** ("update or insert"), and use the [pouchdb-upsert](https://github.com/pouchdb/pouchdb-upsert) plugin to implement it:
+
+```js
+function myDeltaFunction(doc) {
+ doc.counter = doc.counter || 0;
+ doc.counter++;
+ return doc;
+}
+
+db.upsert('my_id', myDeltaFunction).then(function () {
+ // success!
+}).catch(function (err) {
+ // error (not a 404 or 409)
+});
+```
+
+This `upsert()` function takes a `docId` and `deltaFunction`, where the `deltaFunction` is just a function that takes a document and outputs a new document. (If the document does not exist, then an empty document is provided.)
+
+`pouchdb-upsert` also offers a `putIfNotExists()` function, which will create a document if it doesn't exist already. For more details, see [the plugin's documentation](https://github.com/pouchdb/pouchdb-upsert#readme).
+
+### Eventual conflicts
+
+Now, let's move on to the second type: **eventual conflicts**.
+
+Imagine two PouchDB databases have both gone offline. The two separate users each make modifications to the same document, and then they come back online at a later time.
+
+Both users committed changes to the same version of the document, and their local databases did not throw 409 errors. What happens then?
+
+This is the classic "conflict" scenario, and CouchDB handles it very elegantly. By default, CouchDB will choose an arbitrary winner based on a deterministic algorithm, which means both users will see the same winner once they're back online. However, since the replication history is stored, you can always go back in time to resolve the conflict.
+
+To detect if a document is in conflict, you use the `{conflicts: true}` option when you `get()` it.
+
+```js
+db.get('docid', {conflicts: true}).then(function (doc) {
+ // do something with the doc
+}).catch(function (err) {
+ // handle any errors
+});
+```
+
+If the document has conflicts, then the `doc` will be returned with a `_conflicts` attribute, which may contain the revision IDs of conflicting revisions.
+
+For instance, imagine the `doc` returned is the following:
+
+```js
+{
+ "_id": "docid",
+ "_rev": "2-x",
+ "_conflicts": ["2-y"]
+}
+```
+
+Here we have two separate revisions (`2-x` and `2-y`) written by two separate databases, and one database's revision (`2-x`) has arbitrarily won.
+
+{% include alert/start.html variant="warning" %}
+{% markdown %}
+
+Normally, `_rev`s look more like `2-c1592ce7b31cc26e91d2f2029c57e621`, i.e. a digit followed by a very long hash. In these examples, `x` and `y` are used in place of the hash, for simplicity's sake.
+
+{% endmarkdown %}
+{% include alert/end.html %}
+
+Notice that the document's current revision starts with `2-`, and the conflicting version also starts with `2-`, indicating that they're both at the same level of the revision tree. (Revision hashes start with `1-`, `2-`, `3-`, etc., which indicates their distance from the first, "root" revision. The root always starts with `1-`.)
+
+Both databases will see the same conflict, assuming replication has completed. In fact, all databases in the network will see the exact same revision history – much like Git.
+
+To fetch the losing revision, you simply `get()` it using the `rev` option:
+
+```js
+db.get('docid', {rev: '2-y'}).then(function (doc) {
+ // do something with the doc
+}).catch(function (err) {
+ // handle any errors
+});
+```
+
+At this point, you can present both versions to the user, or resolve the conflict automatically using your preferred conflict resolution strategy: last write wins, first write wins, [RCS](https://www.gnu.org/software/rcs/), etc.
+
+To mark a conflict as resolved, all you need to do is `remove()` the unwanted revisions. So for instance, to remove `'2-y'`, you would do:
+
+```js
+db.remove('docid', '2-y').then(function (doc) {
+ // yay, we're done
+}).catch(function (err) {
+ // handle any errors
+});
+```
+
+If you want to resolve the conflict by creating a new revision, you simply `put()` a new document on top of the current winner, and make sure that the losing revision is deleted.
+
+{% include anchor.html title="Accountants don't use erasers" hash="accountants-dont-use-erasers" %}
+
+Another conflict resolution strategy is to design your database so that conflicts are impossible. In practice, this means that you never update or remove existing documents – you only create new documents.
+
+This strategy has been called the "every doc is a delta" strategy. A classic use-case for this would be a checkbook app, where every document is simply an operation that increases or decreases the account balance:
+
+```js
+{_id: new Date().toJSON(), change: 100} // balance increased by $100
+{_id: new Date().toJSON(), change: -50} // balance decreased by $50
+{_id: new Date().toJSON(), change: 200} // balance increased by $200
+```
+
+In this system, it is impossible for two documents to conflict, because the document `_id`s are just timestamps. Ledger transactions are recorded in the order they were made, and at the end of the day, you only need to do an `allDocs()` or `query()` operation to sum the result.
+
+The wisdom of this strategy can be expressed by the maxim: ["Accountants don't use erasers"](https://queue.acm.org/detail.cfm?id=2884038). Like a diligent accountant, your app can just add new documents when you want to make a change, rather than going back and scrubbing out previous changes.
+
+There is also a PouchDB plugin that implements this strategy: [delta-pouch](https://github.com/redgeoff/delta-pouch).
+
+{% include anchor.html title="Next" hash="next" %}
+
+Now that we've settled our conflicts, let's take a look at the changes feed.
diff --git a/docs/version/8.0.0/guides/databases.md b/docs/version/8.0.0/guides/databases.md
new file mode 100644
index 0000000000..6e1052799c
--- /dev/null
+++ b/docs/version/8.0.0/guides/databases.md
@@ -0,0 +1,146 @@
+---
+index: 4
+layout: guide.html
+title: Working with databases
+sidebar: guides_nav.html
+---
+
+PouchDB databases come in two flavors: local and remote.
+
+{% include anchor.html title="Local databases" hash="local–databases" %}
+
+To create a local database, you simply call `new PouchDB` and give it a name:
+
+```js
+const db = new PouchDB('kittens');
+```
+
+You can see a **[live example](http://bl.ocks.org/nolanlawson/bddac54b92c2d8d39241)** of this code.
+
+{% include alert/start.html variant="info" %}
+
+Protip: whenever you see a live example in this guide, you can download it to follow along at home! For example, to run this example, just enter the following commands in your command prompt:
+
+
+ git clone https://gist.github.com/bddac54b92c2d8d39241.git kittens
+ cd kittens
+ python -m SimpleHTTPServer # for Python 2
+ python -m http.server # for Python 3
+
+
+Now the site is up and running at http://localhost:8000. To find the correct gist.github.com URL, just click the "block" number at the top of the page.
+
+{% include alert/end.html %}
+
+{% include anchor.html title="Remote databases" hash="remote-databases" %}
+
+To create a remote database, you call `new PouchDB` and give it a path to a database in CouchDB.
+
+```js
+const db = new PouchDB('http://localhost:5984/kittens');
+```
+
+{% include alert/start.html variant="info" %}
+note: The remote database will not be created until you do an API call, e.g.: db.info(). The reason behind that is that the PouchDB constructor is completely
+synchronous, for ease of error handling (i.e. no asynchronous errors).
+{% include alert/end.html %}
+
+The structure of a CouchDB URL is very simple:
+
+```
+http:// localhost:5984 /kittens
+⌞_____⌟ ⌞____________⌟ ⌞_____⌟
+ | | |
+Protocol Where CouchDB database
+(https if itself is name
+Cloudant) hosted
+
+```
+
+If the remote database doesn't exist, then PouchDB will create it for you.
+
+You can verify that your database is working by visiting the URL [http://localhost:5984/kittens](http://localhost:5984/kittens). You should see something like this:
+
+```js
+{"db_name":"kittens","doc_count":0,"doc_del_count":0,"update_seq":0,"purge_seq":0,"compact_running":false,"disk_size":79,"data_size":0,"instance_start_time":"1410722558431975","disk_format_version":6,"committed_update_seq":0}
+```
+
+If instead you see:
+
+```js
+{"error":"not_found","reason":"no_db_file"}
+```
+
+Then check to make sure that your remote CouchDB has started up correctly. Common errors (such as CORS) are [listed here](/errors.html).
+
+
+{% include anchor.html title="Get basic info about the database" hash="get-basic-info-about-the–database" %}
+
+You can see basic information about the database by using the `info()` method.
+
+```js
+db.info().then(function (info) {
+ console.log(info);
+})
+```
+
+The local database should show something like:
+
+```js
+{"doc_count":0,"update_seq":0,"db_name":"kittens"}
+```
+
+The remote database may have a bit more information:
+
+```js
+{"db_name":"kittens","doc_count":0,"doc_del_count":0,"update_seq":0,"purge_seq":0,"compact_running":false,"disk_size":79,"data_size":0,"instance_start_time":"1410722558431975","disk_format_version":6,"committed_update_seq":0}
+```
+
+The most important bits of information are:
+
+* `doc_count`: the number of undeleted documents in the database
+* `db_name`: the name of the database
+
+
+{% include anchor.html title="Debugging" hash="debugging" %}
+
+### IndexedDB/WebSQL inspectors
+
+You can use the normal developer tools to see what your database looks like under the hood.
+
+In Chrome, just choose *Overflow icon* ☰ → *Tools* → *Developer Tools*. Then click the *Resources* tab, then *IndexedDB*, and you should see the following:
+
+{% include img.html src="dev_tools.png" alt="Chrome Developer Tools" %}
+
+This is the raw IndexedDB representation of your PouchDB, so it is very fine-grained. However, you may find it useful.
+
+In Safari, your database will be under *Develop* → *Show Web Inspector* → *Resources* → *Databases*.
+
+{% include img.html src="safari_inspector.png" alt="Web Inspector in Safari" %}
+
+{% include anchor.html title="Deleting your local database" hash="deleting-your-local-database" %}
+
+During development, it's often useful to destroy the local database, so you can see what your users will experience when they visit your site for the first time. A page refresh is not enough, because the data will still be there!
+
+In Chrome, you can use the [Clear Cache extension](https://chrome.google.com/webstore/detail/clear-cache/cppjkneekbjaeellbfkmgnhonkkjfpdn), which will add a trashcan icon to your toolbar, which you can click to delete all local data (IndexedDB, WebSQL, LocalStorage, cookies, etc.).
+
+In Firefox, you can use the [Clear Browsing Data add-on](https://addons.mozilla.org/en-US/firefox/addon/clear-browsing-data/), which adds a toolbar button to delete your IndexedDB with one click.
+
+In Safari, you can simply click *Safari* → *Clear History and Website Data*.
+
+{% include anchor.html title="Differences between the local and remote databases" hash="differences-between-the-local-and-remote-databases" %}
+
+When you create a local PouchDB database, it uses whatever underlying datastore is available - IndexedDB in most browsers, WebSQL in older browsers, and LevelDB in Node.js.
+
+When you create a remote PouchDB database, it communicates directly with the remote database – CouchDB, Cloudant, Couchbase, etc.
+
+The goal of PouchDB is to allow you to seamlessly communicate with one or the other. You should not notice many differences between the two, except that of course the local one is much faster!
+
+{% include anchor.html title="Related API documentation" hash="related-api-documentation" %}
+
+* [new PouchDB() (constructor)](/api.html#create_database)
+* [Debug mode](/api.html#debug_mode)
+
+{% include anchor.html title="Next" hash="next" %}
+
+Now that you've created some databases, let's put some documents in 'em!
diff --git a/docs/version/8.0.0/guides/documents.md b/docs/version/8.0.0/guides/documents.md
new file mode 100644
index 0000000000..4b3676e775
--- /dev/null
+++ b/docs/version/8.0.0/guides/documents.md
@@ -0,0 +1,191 @@
+---
+index: 5
+layout: guide.html
+title: Working with documents
+sidebar: guides_nav.html
+---
+
+{% include anchor.html title="What's a document?" hash="whats-a–document" %}
+
+PouchDB is a NoSQL database, meaning that you store unstructured *documents* rather than explicitly specifying a schema with rows, tables, and all that jazz.
+
+A document might look like this:
+
+```js
+{
+ "_id": "mittens",
+ "name": "Mittens",
+ "occupation": "kitten",
+ "age": 3,
+ "hobbies": [
+ "playing with balls of yarn",
+ "chasing laser pointers",
+ "lookin' hella cute"
+ ]
+}
+```
+
+If you come from a SQL background, this handy conversion chart may help:
+
+
+
+
+
SQL concept
+
PouchDB concept
+
+
+
table
+
no equivalent
+
+
+
row
+
document
+
+
+
+
column
+
field
+
+
+
primary key
+
primary key (_id)
+
+
+
index
+
view
+
+
+
+
+We'll discuss these concepts later on.
+
+{% include anchor.html title="Storing a document" hash="storing-a–document" %}
+
+To store a document, you simply `put` it:
+
+```js
+const doc = {
+ "_id": "mittens",
+ "name": "Mittens",
+ "occupation": "kitten",
+ "age": 3,
+ "hobbies": [
+ "playing with balls of yarn",
+ "chasing laser pointers",
+ "lookin' hella cute"
+ ]
+};
+db.put(doc);
+```
+
+Whenever you `put()` a document, it must have an `_id` field so that you can retrieve it later.
+
+So now let's `get()` the document by using its `_id`:
+
+```js
+db.get('mittens').then(function (doc) {
+ console.log(doc);
+});
+```
+
+You should see:
+
+```js
+{
+ "name": "Mittens",
+ "occupation": "kitten",
+ "age": 3,
+ "hobbies": [
+ "playing with balls of yarn",
+ "chasing laser pointers",
+ "lookin' hella cute"
+ ],
+ "_id": "mittens",
+ "_rev": "1-bea5fa18e06522d12026f4aee6b15ee4"
+}
+```
+
+You can see a **[live example](http://bl.ocks.org/nolanlawson/c02bba75247012afb1bf)** of this code.
+
+The document looks exactly the same as when we put it, except... aha! What is this? There is a new field, `_rev`, that contains what looks like garbage. PouchDB gots some 'splainin' to do.
+
+{% include anchor.html title="Understanding revisions (`_rev`)" hash="understanding-revisions-rev" %}
+
+The new field, `_rev` is the *revision marker*. It is a randomly-generated ID that changes whenever a document is created or updated.
+
+Unlike most other databases, whenever you update a document in PouchDB or CouchDB, you must present the *entire document* along with its current *revision marker*.
+
+For instance, to increment Mittens' age to 4, we would do:
+
+```js
+doc.age = 4;
+doc._rev = "1-bea5fa18e06522d12026f4aee6b15ee4";
+db.put(doc);
+```
+
+If you fail to include the correct `_rev`, you will get the following sad error:
+
+```js
+{
+ "status": 409,
+ "name": "conflict",
+ "message": "Document update conflict"
+}
+```
+
+`HTTP 409` is a standard HTTP error message that indicates a conflict.
+
+{% include anchor.html title="Updating documents correctly" hash="updating-documents–correctly" %}
+
+So to update Mittens' age, we will first need to fetch Mittens from the database, to ensure that we have the correct `_rev` before we put them back. We don't need to manually assign the `_rev` value here (like we did above), as it is already in the `doc` we're fetching.
+
+```js
+// fetch mittens
+db.get('mittens').then(function (doc) {
+ // update their age
+ doc.age = 4;
+ // put them back
+ return db.put(doc);
+}).then(function () {
+ // fetch mittens again
+ return db.get('mittens');
+}).then(function (doc) {
+ console.log(doc);
+});
+```
+
+You can see a **[live example](http://bl.ocks.org/nolanlawson/d6daa02ca3875d1222dd)** of this code.
+
+{% include alert/start.html variant="info" %}
+
+Don't worry if the structure of this code seems strange! It's using promises, which will be discussed in the next chapter.
+
+{% include alert/end.html %}
+
+Now you should see the following:
+
+```js
+{
+ "name": "Mittens",
+ "occupation": "kitten",
+ "age": 4,
+ "hobbies": [
+ "playing with balls of yarn",
+ "chasing laser pointers",
+ "lookin' hella cute"
+ ],
+ "_id": "mittens",
+ "_rev": "2-3e3fd988b331193beeeea2d4221b57e7"
+}
+```
+
+As you can see, we have successfully updated Mittens' age to 4 (they grow up so fast!), and their revision marker has also changed to `"2-3e3fd988b331193beeeea2d4221b57e7"`. If we wanted to increment their age to 5, we would need to supply this new revision marker.
+
+{% include anchor.html title="Related API documentation" hash="related-api-documentation" %}
+
+* [get()](/api.html#fetch_document)
+* [put()](/api.html#create_document)
+
+{% include anchor.html title="Next" hash="next" %}
+
+Now that you understand a bit about how to create and update documents, let's take a small detour to talk about asynchronous code.
diff --git a/docs/version/8.0.0/guides/guides.json b/docs/version/8.0.0/guides/guides.json
new file mode 100644
index 0000000000..cd20b85a28
--- /dev/null
+++ b/docs/version/8.0.0/guides/guides.json
@@ -0,0 +1,3 @@
+{
+ "tags": "guides"
+}
diff --git a/docs/version/8.0.0/guides/index.md b/docs/version/8.0.0/guides/index.md
new file mode 100644
index 0000000000..b22939bcdb
--- /dev/null
+++ b/docs/version/8.0.0/guides/index.md
@@ -0,0 +1,56 @@
+---
+index: 1
+layout: guide.html
+nav: Intro
+title: Introduction to PouchDB
+sidebar: guides_nav.html
+---
+
+Welcome to the PouchDB guide! Consider this your starting point for anything and everything related to the world of PouchDB and CouchDB.
+
+For a quicker TodoMVC-based tutorial, you can also check out the ["Getting Started" guide]({{ site.baseurl }}/getting-started.html).
+
+Feel free to skip ahead using the sidebar at any time.
+
+{% include anchor.html title="What is PouchDB?" hash="what-is-pouchdb" %}
+
+**PouchDB** is a JavaScript implementation of [CouchDB](https://couchdb.apache.org). Its goal is to emulate the CouchDB API with near-perfect fidelity, while running in the browser or in Node.js.
+
+{% include anchor.html title="What is CouchDB?" hash="what-is-couchdb" %}
+
+**CouchDB** is a NoSQL database created in 2005 by Damien Katz, and now maintained by the Apache Software Foundation. If you are a JavaScript developer, you probably use CouchDB every day, because it's the core technology that powers [npm](https://www.npmjs.org/).
+
+{% include anchor.html title="Couchbase, CouchDB, Couch-what?" hash="couchbase-couchdb-couch-what" %}
+
+Today there are two major database companies that
+can trace their lineage back to CouchDB: [**Couchbase**](http://couchbase.com) and [**Cloudant**](http://cloudant.com). Both of them are separate products compared to CouchDB.
+
+However, all three of these databases share the same **CouchDB sync protocol**. This means that PouchDB can sync with either one of them, and you can always swap out one database for another. You're never locked in.
+
+In a sense, these databases are like competing phone companies, and the CouchDB sync protocol is the underlying telephony infrastructure.
+
+{% include anchor.html title="CouchDB's one-two punch: HTTP and sync" hash="http-and-sync" %}
+
+With so many SQL and NoSQL databases out there – MongoDB, PostgreSQL, MySQL, etc. – you may wonder why we chose to implement CouchDB instead of the others.
+
+We have two very good answers to that question: **HTTP** and **sync**.
+
+{% include anchor.html title="HTTP: the little protocol that could" hash="http" %}
+
+When working with databases, we're often accustomed to writing some kind of conversion layer between the database and our client-side applications. This means, however, that we are just translating database queries into RESTful HTTP calls, over and over. For every app we write.
+
+CouchDB throws this out the window by daring us to talk to the database directly, from our client-side apps. And it does so by using HTTP as its primary means of communication. No special protocol, no special drivers: just REST and HTTP. You can communicate with CouchDB entirely through your browser, `curl`, or a REST client like [Postman](https://chrome.google.com/webstore/detail/postman-rest-client/fdmmgilgnpjigdojojpjoooidkmcomcm).
+
+In this way, CouchDB truly is a "database for the web."
+
+{% include anchor.html title="Sync: CouchDB's killer feature" hash="sync" %}
+
+Another unique feature of CouchDB is that it was designed from the bottom-up to enable easy synchronization between different databases.
+
+For example, if you are worried about latency in your client-side applications, you can simply set up one CouchDB in Europe, another in North America, and another in Asia. After enabling continuous two-way replication between these databases, your clients can simply talk to whichever one is closer.
+
+PouchDB takes this one step further by putting the database inside your browser.
+
+{% include anchor.html title="Next" hash="next" %}
+
+Now that you understand the basics of the PouchDB/CouchDB universe, let's set up CouchDB!
diff --git a/docs/version/8.0.0/guides/local-documents.md b/docs/version/8.0.0/guides/local-documents.md
new file mode 100644
index 0000000000..e54a6c9158
--- /dev/null
+++ b/docs/version/8.0.0/guides/local-documents.md
@@ -0,0 +1,44 @@
+---
+index: 16
+layout: guide.html
+title: Local documents
+sidebar: guides_nav.html
+---
+
+"Local" documents are a special class of documents in PouchDB and CouchDB, which are used for storing local metadata about a database. You might never need them in your own app, but sometimes they can come in handy for advanced use cases.
+
+{% include anchor.html title="Local docs in a nutshell" hash="local-docs-in-a-nutshell" %}
+
+Local docs have the following characteristics:
+
+* They don't replicate.
+* They can't contain attachments.
+* They don't appear in `allDocs()`, `changes()`, or `query()`.
+* However, you can modify them with `put()`/`remove()`/`bulkDocs()`, and you can fetch them with `get()`.
+
+So basically, local docs only exist *for that database*, and they don't mix with the "normal" documents.
+
+To create a local doc, you simply use `'_local/'` as the prefix of the `_id`. This is supported in both CouchDB and PouchDB:
+
+```js
+db.put({
+ _id: '_local/foobar',
+ someText: 'yo, this is my local doc!'
+}).then(function () {
+ return db.get('_local/foobar');
+});
+```
+
+{% include anchor.html title="Advantages of local docs" hash="advantages-of-local–docs" %}
+
+Local docs are useful for small bits of configuration or metadata, which you don't necessarily want to replicate, but which you want to keep in the database anyway. Many PouchDB plugins and core components use local docs. For instance, the replication algorithm uses them to store checkpoints, and map/reduce uses them to keep track of what's been `emit`ted.
+
+Local docs also have some good performance characteristics compared to regular docs. They don't have a version history, so only the most recent revision is ever stored in the database. This means that `put()`s and `get()`s are faster for local docs than for regular docs, and that local docs tend to take up less space on disk. In a sense, they are auto-compacted, although they take up even less space on disk than documents in a compacted database.
+
+Regardless, you need to provide the current `_rev` when you update local docs, just like with regular docs.
+
+{% include anchor.html title="Related API documentation" hash="related-api-documentation" %}
+
+* [put()](/api.html#create_document)
+* [get()](/api.html#fetch_document)
+* [remove()](/api.html#delete_document)
diff --git a/docs/version/8.0.0/guides/mango-queries.md b/docs/version/8.0.0/guides/mango-queries.md
new file mode 100644
index 0000000000..64ad8752e4
--- /dev/null
+++ b/docs/version/8.0.0/guides/mango-queries.md
@@ -0,0 +1,364 @@
+---
+index: 13
+layout: guide.html
+title: Mango queries
+sidebar: guides_nav.html
+---
+
+Mango queries, also known as `pouchdb-find` or the `find()` API, are a structured query API that allows you to build _secondary indexes_ beyond the built-in `allDocs()` and `changes()` indexes.
+
+This API is useful for answering questions like:
+
+- find all documents where the `type` is `'user'`
+- find all users whose `age` is greater than `21`
+- find all Pokémon whose `name` starts with `'pika'`
+- etc.
+
+{% include anchor.html title="Installation" hash="installation" %}
+
+The `find()` API is currently offered as a separate plugin, meaning that you must install it on top of `pouchdb.js`. Here's how to do so:
+
+### Script tags
+
+```html
+
+
+```
+
+The `pouchdb.find.js` file is available in the `pouchdb` package in npm/Bower, on [unpkg](https://unpkg.com/pouchdb/dist/), or [as a GitHub download](https://github.com/apache/pouchdb/releases/download/{{ site.version }}/pouchdb.find.js). Note it must be placed after `pouchdb.js`.
+
+### npm
+
+If you are using Node, Browserify, Webpack, Rollup, etc., then you can install it like so:
+
+```bash
+npm install --save pouchdb-find
+```
+
+Then in code:
+
+```js
+const PouchDB = require('pouchdb');
+PouchDB.plugin(require('pouchdb-find'));
+```
+
+{% include anchor.html title="Query language" hash="query-language" %}
+
+The [Mango query language](https://github.com/cloudant/mango) is a DSL inspired by MongoDB, which allows you to define an index that is then used for querying. One quick way to understand how this works is to use the [live query demo](https://nolanlawson.github.io/pouchdb-find/).
+
+At a basic level, there are two steps to running a query: `createIndex()` (to define which fields to index) and `find()` (to query the index).
+
+For instance, let's imagine a simple index to look up all documents whose `name` is `"mario"`. First we'll create it:
+
+```js
+db.createIndex({
+ index: {fields: ['name']}
+});
+```
+
+This returns a Promise that resolves once the index is created. At this point, we have an index based on the `"name"` field, so we can use it for lookup:
+
+```js
+db.find({
+ selector: {
+ name: 'mario'
+ }
+});
+```
+
+This returns a Promise containing an array of all documents that match this selector. Note that this is equivalent to using the `$eq` (equals) operator:
+
+```js
+db.find({
+ selector: {
+ name: {$eq: 'mario'}
+ }
+});
+```
+
+The important thing to understand is that, for a typical database, `createIndex()` is the expensive operation, because it is looping through all documents in the database and building a [B-tree](https://en.wikipedia.org/wiki/B-tree) based on the `name` value.
+
+Once the B-tree is built up, though, the `find()` is relatively cheap. (If this were _not_ the case, then we would be better off just using `allDocs()` to iterate through the database ourselves!)
+
+Once we have an index on `name`, we can also sort all documents by `name`:
+
+```js
+db.find({
+ selector: {
+ name: {$gte: null}
+ },
+ sort: ['name']
+});
+```
+
+Note that we are specifying that the `name` must be greater than or equal to `null`, which is a workaround for the fact that the Mango query language requires us to have a selector. In [CouchDB collation order](https://docs.couchdb.org/en/stable/ddocs/views/collation.html), `null` is the "lowest" value, and so this will return all documents regardless of their `name` value.
+
+{% include anchor.html title="Pagination" hash="pagination" %}
+
+Reading all documents in the database and sorting them by a particular value is neat, but we could do this ourselves with `allDocs()`, and it would have the same performance impact. Where it gets more interesting is when we use `limit`:
+
+```js
+db.find({
+ selector: {
+ name: {$gte: null}
+ },
+ sort: ['name'],
+ limit: 10
+});
+```
+
+In this case, we only get 10 documents back, but they are the first 10 documents, sorted by name. This means that we have only read 10 documents out of the database into memory, which can be used for [efficient pagination]({{ site.baseurl }}/2014/04/14/pagination-strategies-with-pouchdb.html).
+
+For instance, if we are displaying the first 10 results on a single page, and the user clicks "next" to see the next page, we can restructure our query based on the last result, to continue the pagination. Let's imagine the first 10 documents' `name`s are:
+
+```js
+[
+ 'abby', 'bertrand', 'clarice', 'don', 'emily',
+ 'fumiko', 'gunther', 'horatio', 'ike', 'joy'
+]
+```
+
+For our next 10 pages of results, the query becomes:
+
+```js
+db.find({
+ selector: {
+ name: {$gt: 'joy'}
+ },
+ sort: ['name'],
+ limit: 10
+});
+```
+
+Because we are now specifying that the `name` must be greater than `'joy'`, we are guaranteed to get the next-highest result after `'joy'`, which may (for instance) look like this:
+
+```js
+[
+ 'kim', 'lin', 'maria', 'nell', 'oliver',
+ 'pat', 'quincy', 'roy', 'sam', 'tanya'
+]
+```
+
+In this way, we can continue paginating by using the last value as our next starting point. At any given point in time, there are only 10 documents stored in memory at once, which is great for performance.
+
+{% include anchor.html title="Indexing on more than one field" hash="more-than-one-field" %}
+
+Sometimes an index is not as simple as "find all documents whose `name` is `"mario"`. Sometimes you want to do something fancy, such as "find all documents whose `name` is `"mario"` and whose `age` is greater than `21`". In those cases, you can index on more than one field:
+
+```js
+db.createIndex({
+ index: {
+ fields: ['name', 'age']
+ }
+}).then(function () {
+ return db.find({
+ selector: {
+ name: 'mario',
+ age: {$gt: 21}
+ }
+ });
+});
+```
+
+One thing to note is that the order of these fields matters when creating your index. For instance, the following would _not_ work:
+
+```js
+/* THIS WON'T WORK! */
+db.createIndex({
+ index: {
+ fields: ['age', 'name']
+ }
+}).then(function () {
+ return db.find({
+ selector: {
+ name: 'mario',
+ age: {$gt: 21}
+ }
+ });
+});
+```
+
+The reason for this is easy to understand if we imagine how this index would sort a hypothetical database:
+
+
+
+
+
name
+
age
+
+
+
Luigi
+
17
+
+
+
Luigi
+
28
+
+
+
Mario
+
18
+
+
+
Mario
+
21
+
+
+
Mario
+
22
+
+
+
Mario
+
26
+
+
+
Peach
+
17
+
+
+
Peach
+
21
+
+
+
Peach
+
25
+
+
+
+
+In the above table, the documents are sorted by `['name', 'age']`, and our "Marios above the age of 21" are very clearly grouped together.
+
+However, if we were to change the order, and sort them by `['age', 'name']`, it would look instead like this:
+
+
+
+
+
age
+
name
+
+
+
17
+
Luigi
+
+
+
17
+
Peach
+
+
+
18
+
Mario
+
+
+
21
+
Mario
+
+
+
21
+
Peach
+
+
+
22
+
Mario
+
+
+
25
+
Peach
+
+
+
26
+
Mario
+
+
+
28
+
Luigi
+
+
+
+
+If we imagine our `find()` query as a "slice" of the data, it's obvious that there's no slice that corresponds to "all Marios whose age is greater than 21." Instead, our documents are sorted by `age`, and then documents with the same `age` are sorted by `name`.
+
+This index may be good for answering questions like "find all 17-year-olds whose name starts with letters N-Z", but it's not very good for answering questions like "find all people with a certain name, older than a certain age."
+
+This shows that it's important to carefully design an index before creating a query to use that index. Otherwise, the query planner may fall back to in-memory querying, which can be expensive.
+
+{% include anchor.html title="Performance notes" hash="performance-notes" %}
+
+The Mango query language is generally very permissive, and allows you to write queries that may not perform very well, but will run regardless. For instance, you may create an index with `createIndex()`, but then write a `find()` query that doesn't actually use that index. In general, the query planner tries to find the most appropriate index, but it may fall back to in-memory querying.
+
+As a straightforward example, if you query using the `_id` field, then the query planner will automatically map that directly to an `allDocs()` query. However, if you query for a field that isn't yet indexed, then it will simply use `allDocs()` to read in all documents from the database (!) and then filter in-memory. This can lead to poor performance, especially if your database is large.
+
+If you're ever wondering how the query planner is interpreting your query, you can use the explain endpoint:
+
+```js
+db.explain({
+ selector: {
+ name: 'mario',
+ age: {$gt: 21}
+ }
+})
+.then(function (explained) {
+ // detailed explained info can be viewed
+});
+
+```
+
+In the console, the query planner will show a detailed explanation of how it has interpreted the query, whether it uses any indexes, and whether any parts of the query need to be executed in-memory.
+
+
+You may also want to pay attention to the `"warning"` value included in your results set, indicating that there was no index that matched the given query. For instance, the warning may look like this:
+
+```js
+{
+ "docs": [ /* ... */ ],
+ "warning": "No matching index found, create an index to optimize query time."
+}
+```
+
+{% include anchor.html title="Set which index to use" hash="use_index" %}
+
+When creating a query, by settings the `use_index` field, it is possible to tell pouchdb-find which index to use.
+The below example shows how to do that.
+
+```js
+db.createIndex({
+ index: {
+ fields: ['age', 'name'],
+ ddoc: "my-index-design-doc"
+ }
+}).then(function () {
+ return db.find({
+ selector: {
+ name: 'mario',
+ age: {$gt: 21},
+ },
+ use_index: 'my-index-design-doc'
+ });
+});
+```
+
+{% include anchor.html title="Further reading" hash="further-reading" %}
+
+The Mango query language is quite large and supports many options. Some of the more common ones include:
+
+* `$eq`: equals
+* `$gt`: greater than
+* `$gte`: greater than or equal to
+* `$lt`: less than
+* `$lte`: less than or equal to
+
+There are many more options besides these, although note that not all of them can take advantage of indexes. For instance, `$regex`, `$ne`, and `$not` cannot use on-disk indexes, and must use in-memory filtering instead.
+
+The most complete documentation for selector options can be found in the [CouchDB `_find` documentation](https://docs.couchdb.org/en/stable/api/database/find.html). You might also look at the [Cloudant Query Language](https://docs.cloudant.com/cloudant_query.html) documentation (which is nearly identical to Mango, other than `text` and other Cloudant-specific features). PouchDB uses CouchDB as the reference implementation; they ought to be functionally identical.
+
+It should be noted that, over HTTP, this API currently works with CouchDB 2.0+, Cloudant, and PouchDB Server.
+CouchDB 2.0 is the reference implementation, so the API should be the same. CouchDB 1.6.1 and below is not supported.
+
+{% include anchor.html title="Related API documentation" hash="related-api-documentation" %}
+
+* [createIndex()](/api.html#create_index)
+* [find()](/api.html#query_index)
+* [getIndexes()](/api.html#list_indexes)
+* [deleteIndex()](/api.html#delete_index)
+
+{% include anchor.html title="Next" hash="next" %}
+
+Now that we've learned how to do structured Mango queries, let's try some more advanced queries, using _map/reduce_.
diff --git a/docs/version/8.0.0/guides/queries.md b/docs/version/8.0.0/guides/queries.md
new file mode 100644
index 0000000000..46bcfb409a
--- /dev/null
+++ b/docs/version/8.0.0/guides/queries.md
@@ -0,0 +1,240 @@
+---
+index: 14
+layout: guide.html
+title: Map/reduce queries
+sidebar: guides_nav.html
+---
+
+Map/reduce queries, also known as the `query()` API, are one of the most powerful features in PouchDB. However, they can be quite tricky to use, and so this guide is designed to dispell some of the mysteries around them.
+
+The first thing to understand is that you don't need map/reduce queries if you merely want to look up documents by `_id` or sort them by `_id`. The `allDocs()` API already does this, using an efficient built-in index (see ["bulk operations"](bulk-operations.html) for details).
+
+The second thing to know is that map/reduce is also unnecessary if you want to sort documents by their update time – this is exactly what the [changes feed](changes.html) does! Again, this is a built-in index that you get for free.
+
+Finally, it's important to understand that [Mango queries](mango-queries.html) are much easier to use than map/reduce queries, and they can usually satisfy 99% of use cases. The point of map/reduce is to provide an _extremely advanced_ API for building secondary indexes, suitable for those with specific querying needs.
+
+So now that you've read the fine print, let's talk about how map/reduce queries actually work!
+
+{% include anchor.html title="Mappin' and reducin'" hash="mappin-and-reducin" %}
+
+The PouchDB `query()` API (which corresponds to the `_view` API in CouchDB) has two modes: temporary queries and persistent queries.
+
+### Temporary queries
+
+**Temporary queries** are very slow, and we only recommend them for quick debugging during development. To use a temporary query, you simply pass in a `map` function:
+
+```js
+db.query(function (doc, emit) {
+ emit(doc.name);
+}, {key: 'foo'}).then(function (result) {
+ // found docs with name === 'foo'
+}).catch(function (err) {
+ // handle any errors
+});
+```
+
+In the above example, the `result` object will contain stubs of documents where the `name` attribute is equal to `'foo'`. To include the document in each row of results, use the `include_docs` option.
+
+{% include alert/start.html variant="info" %}
+
+The emit pattern is part of the standard CouchDB map/reduce API. What the function basically says is, "for each document, emit doc.name as a key."
+
+{% include alert/end.html %}
+
+### Persistent queries
+
+**Persistent queries** are much faster, and are the intended way to use the `query()` API in your production apps. To use persistent queries, there are two steps.
+
+First, you create a **design document**, which describes the `map` function you would like to use:
+
+```js
+// document that tells PouchDB/CouchDB
+// to build up an index on doc.name
+const ddoc = {
+ _id: '_design/my_index',
+ views: {
+ by_name: {
+ map: function (doc) { emit(doc.name); }.toString()
+ }
+ }
+};
+// save it
+pouch.put(ddoc).then(function () {
+ // success!
+}).catch(function (err) {
+ // some error (maybe a 409, because it already exists?)
+});
+```
+
+{% include alert/start.html variant="info" %}
+
+The .toString() at the end of the map function is necessary to prep the
+object for becoming valid JSON.
+
+{% include alert/end.html %}
+
+{% include alert/start.html variant="info" %}
+
+The emit function will be available in scope when the map function is
+run, so don't pass it in as a parameter.
+
+{% include alert/end.html %}
+
+Then you actually query it, by using the name you gave the design document when you saved it:
+
+```js
+db.query('my_index/by_name').then(function (res) {
+ // got the query results
+}).catch(function (err) {
+ // some error
+});
+```
+
+Note that, the first time you query, it will be quite slow because the index isn't
+built until you query it. To get around this, you can do an empty query to kick
+off a new build:
+
+```js
+db.query('my_index/by_name', {
+ limit: 0 // don't return any results
+}).then(function (res) {
+ // index was built!
+}).catch(function (err) {
+ // some error
+});
+```
+
+After this, your queries will be much faster.
+
+{% include alert/start.html variant="info"%}
+
+CouchDB builds indexes in exactly the same way as PouchDB. So you may want to familiarize yourself with the "stale" option in order to get the best possible performance for your app.
+
+{% include alert/end.html %}
+
+
+{% include anchor.html title="More about map/reduce" hash="more-about-map-reduce" %}
+
+That was a fairly whirlwind tour of the `query()` API, so let's get into more detail about how to write your map/reduce functions.
+
+#### Indexes in SQL databases
+
+Quick refresher on how indexes work: in relational databases like MySQL and PostgreSQL, you can usually query whatever field you want:
+
+```sql
+SELECT * FROM pokemon WHERE name = 'Pikachu';
+```
+
+But if you don't want your performance to be terrible, you first add an index:
+
+```sql
+ALTER TABLE pokemon ADD INDEX myIndex ON (name);
+```
+
+The job of the index is to ensure the field is stored in a B-tree within the database, so your queries run in _O(log(n))_ time instead of _O(n)_ time.
+
+#### Indexes in NoSQL databases
+
+All of the above is also true in document stores like CouchDB and MongoDB, but conceptually it's a little different. By default, documents are assumed to be schemaless blobs with one primary key (called `_id` in both Mongo and Couch), and any other keys need to be specified separately. The concepts are largely the same; it's mostly just the vocabulary that's different.
+
+In CouchDB, queries are called _map/reduce functions_. This is because, like most NoSQL databases, CouchDB is designed to scale well across multiple computers, and to perform efficient query operations in parallel. Basically, the idea is that you divide your query into a _map_ function and a _reduce_ function, each of which may be executed in parallel in a multi-node cluster.
+
+#### Map functions
+
+It may sound daunting at first, but in the simplest (and most common) case, you only need the _map_ function. A basic map function might look like this:
+
+```js
+function myMapFunction(doc) {
+ emit(doc.name);
+}
+```
+
+This is functionally equivalent to the SQL index given above. What it essentially says is: "for each document in the database, emit its name as a key."
+
+And since it's just JavaScript, you're allowed to get as fancy as you want here:
+
+```js
+function myMapFunction(doc) {
+ if (doc.type === 'pokemon') {
+ if (doc.name === 'Pikachu') {
+ emit('Pika pi!');
+ } else {
+ emit(doc.name);
+ }
+ }
+}
+```
+
+Then you can query it:
+
+```js
+// find pokemon with name === 'Pika pi!'
+pouch.query(myMapFunction, {
+ key : 'Pika pi!',
+ include_docs : true
+}).then(function (result) {
+ // handle result
+}).catch(function (err) {
+ // handle errors
+});
+
+// find the first 5 pokemon whose name starts with 'P'
+pouch.query(myMapFunction, {
+ startkey : 'P',
+ endkey : 'P\ufff0',
+ limit : 5,
+ include_docs : true
+}).then(function (result) {
+ // handle result
+}).catch(function (err) {
+ // handle errors
+});
+```
+
+{% include alert/start.html variant="info"%}
+
+The pagination options for query() – i.e., startkey/endkey/key/keys/skip/limit/descending – are exactly the same as with allDocs(). For a guide to pagination, read the Bulk operations guide or Pagination strategies with PouchDB.
+
+{% include alert/end.html %}
+
+#### Reduce functions
+
+As for _reduce_ functions, there are a few handy built-ins that do aggregate operations (`'_sum'`, `'_count'`, and `'_stats'`), and you can typically steer clear of trying to write your own:
+
+```js
+// emit the first letter of each pokemon's name
+const myMapReduceFun = {
+ map: function (doc) {
+ emit(doc.name.charAt(0));
+ },
+ reduce: '_count'
+};
+// count the pokemon whose names start with 'P'
+pouch.query(myMapReduceFun, {
+ key: 'P', reduce: true, group: true
+}).then(function (result) {
+ // handle result
+}).catch(function (err) {
+ // handle errors
+});
+```
+
+If you're adventurous, though, you should check out the [CouchDB documentation](http://couchdb.readthedocs.org/en/latest/couchapp/views/intro.html) or the [PouchDB documentation]({{ site.baseurl }}/api.html#query_database) for details on reduce functions.
+
+{% include anchor.html title="Avoiding map/reduce" hash="avoiding-map-reduce" %}
+
+The map/reduce API is complex, and it can be computationally expensive because it requires building up an entirely new index. Therefore, it's good to know some tricks for avoiding the map/reduce API when you don't need it:
+
+1. If you can use `allDocs()` or `changes()` instead of the `query()` API, do it!
+2. If your query is simple enough that you can use `find()`, use that instead.
+3. Read the [12 tips for better code with PouchDB](/2014/06/17/12-pro-tips-for-better-code-with-pouchdb.html), especially the tip to "use and abuse your doc _ids."
+4. If your data is highly relational, try the [relational-pouch](https://github.com/nolanlawson/relational-pouch) plugin, which follows this advice, and only uses `_id` and `allDocs()` under the hood.
+
+{% include anchor.html title="Related API documentation" hash="related-api-documentation" %}
+
+* [query()](/api.html#query_database)
+* [viewCleanup()](/api.html#view_cleanup)
+
+{% include anchor.html title="Next" hash="next" %}
+
+Now that we've learned how to map reduce, map reuse, and map recycle, let's move on to `destroy()` and `compact()`.
diff --git a/docs/version/8.0.0/guides/replication.md b/docs/version/8.0.0/guides/replication.md
new file mode 100644
index 0000000000..4f6a724205
--- /dev/null
+++ b/docs/version/8.0.0/guides/replication.md
@@ -0,0 +1,194 @@
+---
+index: 10
+layout: guide.html
+title: Replication
+sidebar: guides_nav.html
+---
+
+PouchDB and CouchDB were designed for one main purpose: **sync**. Jason Smith has [a great quote](http://nodeup.com/thirtyseven) about this:
+
+> The way I like to think about CouchDB is this: CouchDB is bad at everything, *except syncing*. And it turns out that's the most important feature you could ever ask for, for many types of software."
+
+When you first start using CouchDB, you may become frustrated because it doesn't work quite like other databases. Unlike most databases, CouchDB requires you to manage revisions (`_rev`), which can be tedious.
+
+However, CouchDB was designed with sync in mind, and this is exactly what it excels at. Many of the rough edges of the API serve this larger purpose. For instance, managing your document revisions pays off in the future, when you eventually need to start dealing with conflicts.
+
+{% include anchor.html title="CouchDB sync" hash="couchdb-sync" %}
+
+CouchDB sync has a unique design. Rather than relying on a master/follower architecture, CouchDB
+supports a **multi-master** architecture. You can think of this as a system where any node can be written to or read from, and where you don't have to care which one is the "master" and which one is the "follower." In CouchDB's egalitarian world, every citizen is as worthy as another.
+
+
+ {% include img.html src="offline_replication.gif" alt="Offline replication with CouchDB." %}
+
+
(Thanks to IBM for the image: http://www.ibm.com/developerworks/library/wa-couchdb/)
+
+
+
+When you use PouchDB, CouchDB, and other members of the Couch family, you
+don't have to worry which database is the "single source of truth." They all are. According to the CAP theorem, a database can only have at most 2 of 3 properties: Consistency, Availability, or Partition-Tolerance. Typical relational databases such as MySQL are CP, which means they are consistent and tolerant to node partitions, at the expense of availability. CouchDB is an AP database, meaning that it's **P**artition-Tolerant,
+every node is **A**vailable at all times, but it's only eventually **C**onsistent.
+
+To illustrate, imagine a multi-node architecture with CouchDB servers spread across several continents. As long as you're willing to wait, the data will eventually flow
+from Australia to Europe to North America to wherever. Users around the world running PouchDB in their browsers or [Couchbase Lite](https://github.com/couchbase/couchbase-lite-ios)/[Cloudant Sync](https://github.com/cloudant/CDTDatastore) in their smartphones experience the
+same privileges. The data won't show up instantaneously, but depending on the Internet connection speed, it's usually close enough to real-time.
+
+In cases of conflict, CouchDB will choose an arbitrary winner that every node can agree upon deterministically. However, conflicts are still stored in the **revision tree** (similar to a Git history tree), which means that app developers can either surface the conflicts to the user, or just ignore them.
+
+In this way, CouchDB replication "just works."
+
+{% include anchor.html title="Setting up sync" hash="setting-up-sync" %}
+
+As you already know, you can create either local PouchDBs:
+
+```js
+const localDB = new PouchDB('mylocaldb')
+```
+
+or remote PouchDBs:
+
+```js
+const remoteDB = new PouchDB('http://localhost:5984/myremotedb')
+```
+
+This pattern comes in handy when you want to share data between the two.
+
+The simplest case is **unidirectional replication**, meaning you just want one database to mirror its changes to a second one. Writes to the second database, however, will not propagate back to the master database.
+
+To perform unidirectional replication, you simply do:
+
+```js
+localDB.replicate.to(remoteDB).on('complete', function () {
+ // yay, we're done!
+}).on('error', function (err) {
+ // boo, something went wrong!
+});
+```
+
+Congratulations, all changes from the `localDB` have been replicated to the `remoteDB`.
+
+However, what if you want **bidirectional replication**? You could do:
+
+```js
+localDB.replicate.to(remoteDB);
+localDB.replicate.from(remoteDB);
+```
+
+However, to make things easier for your poor tired fingers, PouchDB has a shortcut API:
+
+```js
+localDB.sync(remoteDB);
+```
+
+These two code blocks above are equivalent. And the `sync` API supports all the same events as the `replicate` API:
+
+```js
+localDB.sync(remoteDB).on('complete', function () {
+ // yay, we're in sync!
+}).on('error', function (err) {
+ // boo, we hit an error!
+});
+```
+
+{% include anchor.html title="Live replication" hash="live–replication" %}
+
+Live replication (or "continuous" replication) is a separate mode where changes are propagated between the two databases as the changes occur. In other words, normal replication happens once, whereas live replication happens in real time.
+
+To enable live replication, you simply specify `{live: true}`:
+
+```js
+localDB.sync(remoteDB, {
+ live: true
+}).on('change', function (change) {
+ // yo, something changed!
+}).on('error', function (err) {
+ // yo, we got an error! (maybe the user went offline?)
+});
+```
+
+However, there is one gotcha with live replication: what if the user goes offline? In those cases, an error will be thrown and replication will stop.
+
+You can allow PouchDB to automatically handle this error, and retry until the connection is re-established, by using the `retry` option:
+
+```js
+localDB.sync(remoteDB, {
+ live: true,
+ retry: true
+}).on('change', function (change) {
+ // yo, something changed!
+}).on('paused', function (info) {
+ // replication was paused, usually because of a lost connection
+}).on('active', function () {
+ // replication was resumed
+}).on('error', function (err) {
+ // totally unhandled error (shouldn't happen)
+});
+```
+
+This is ideal for scenarios where the user may be flitting in and out of connectivity, such as on mobile devices.
+
+{% include anchor.html title="Canceling replication" hash="canceling—replication" %}
+
+Sometimes, you may want to manually cancel replication – for instance, because the user logged out. You can do so by calling `cancel()` and then waiting for the `'complete'` event:
+
+```js
+const syncHandler = localDB.sync(remoteDB, {
+ live: true,
+ retry: true
+});
+
+syncHandler.on('complete', function (info) {
+ // replication was canceled!
+});
+
+syncHandler.cancel(); // <-- this cancels it
+```
+
+The `replicate` API also supports canceling:
+
+```js
+const replicationHandler = localDB.replicate.to(remoteDB, {
+ live: true,
+ retry: true
+});
+
+replicationHandler.on('complete', function (info) {
+ // replication was canceled!
+});
+
+replicationHandler.cancel(); // <-- this cancels it
+```
+
+{% include anchor.html title="Deleting replicated databases" hash="delete-during-replication" %}
+-----
+
+One thing to note about replication is that it tracks the data within a database, not the database itself. If you [`destroy()`](/api.html#delete_database) a database that is being replicated to, the next time the replication starts it will transfer all of the data again, recreating the database to the state it was before it was `destroyed`. If you want the data within the database to be deleted you will need to delete via [`remove()`](/api.html#delete_document) or [`bulkDocs()`](/api.html#batch_create). The [pouchdb-erase](https://github.com/marten-de-vries/pouchdb-erase) plugin can help you remove the entire contents of a database.
+
+{% include anchor.html title="Fancy replication" hash="fancy-replication" %}
+-----
+
+Any PouchDB object can replicate to any other PouchDB object. So for instance, you can replicate two remote databases, or two local databases. You can also replicate from multiple databases into a single one, or from a single database into many others.
+
+This can be very powerful, because it enables lots of fancy scenarios. For example:
+
+1. You have an [in-memory PouchDB]({{ site.baseurl }}/adapters.html#pouchdb_in_the_browser) that replicates with a local PouchDB, acting as a cache.
+2. You have many remote CouchDB databases that the user may access, and they are all replicated to the same local PouchDB.
+3. You have many local PouchDB databases, which are mirrored to a single remote CouchDB as a backup store.
+
+The only limits are your imagination and your disk space.
+
+{% include alert/start.html variant="warning" %}
+
+When you replicate between two remote databases, the changes flow through PouchDB. If this is not what you want, then you should POST directly to the CouchDB _replicate endpoint, as described in the CouchDB replication guide.
+
+{% include alert/end.html %}
+
+
+{% include anchor.html title="Related API documentation" hash="related-api-documentation" %}
+
+* [replication()](/api.html#replication)
+* [sync()](/api.html#sync)
+
+{% include anchor.html title="Next" hash="next" %}
+
+Now that we have a grasp on replication, let's talk about an inconvenient fact of life: conflicts.
diff --git a/docs/version/8.0.0/guides/setup-couchdb.md b/docs/version/8.0.0/guides/setup-couchdb.md
new file mode 100644
index 0000000000..19c962c299
--- /dev/null
+++ b/docs/version/8.0.0/guides/setup-couchdb.md
@@ -0,0 +1,103 @@
+---
+index: 2
+layout: guide.html
+title: Setting up CouchDB
+sidebar: guides_nav.html
+---
+
+{% include anchor.html title="CouchDB: PouchDB's older sibling" hash="couchdb-pouchdbs-older-sibling" %}
+
+One of the main benefits of learning PouchDB is that it's exactly the same as CouchDB. In fact, PouchDB is a shameless plagiarist: all of the API methods are the same, with only slight modifications to make it more JavaScript-y.
+
+For instance, in CouchDB you would fetch all documents using:
+
+```bash
+/db/_all_docs?include_docs=true
+```
+
+In PouchDB this becomes:
+
+```js
+db.allDocs({include_docs: true})
+```
+
+The APIs are the same, and the semantics are the same.
+
+In the following examples, we will set up CouchDB and talk to it using a tool you're already familiar with: your browser.
+
+{% include anchor.html title="Installing CouchDB" hash="installing-couchdb" %}
+
+If you are on a Debian flavor of Linux (Ubuntu, Mint, etc.), you can install CouchDB as follows.
+
+First, [enable the CouchDB package repository](https://docs.couchdb.org/en/stable/install/unix.html#enabling-the-apache-couchdb-package-repository) on your machine:
+
+```bash
+$ sudo apt update && sudo apt install -y curl apt-transport-https gnupg
+$ curl https://couchdb.apache.org/repo/keys.asc | gpg --dearmor | sudo tee /usr/share/keyrings/couchdb-archive-keyring.gpg >/dev/null 2>&1
+source /etc/os-release
+$ echo "deb [signed-by=/usr/share/keyrings/couchdb-archive-keyring.gpg] https://apache.jfrog.io/artifactory/couchdb-deb/ ${VERSION_CODENAME} main" \
+ | sudo tee /etc/apt/sources.list.d/couchdb.list >/dev/null
+```
+
+Next, update your package lists and install CouchDB:
+
+```bash
+$ sudo apt-get update
+$ sudo apt-get install -y couchdb
+```
+
+If you are on a Mac or Windows you should install the official binaries from [the CouchDB web site](https://couchdb.apache.org/#download).
+
+#### A CouchDB alternative: PouchDB Server
+
+If you have trouble installing CouchDB, you can also install PouchDB Server, which is a drop-in replacement for CouchDB that uses PouchDB under the hood:
+
+```bash
+$ npm install -g pouchdb-server
+$ pouchdb-server --port 5984
+```
+
+PouchDB Server is currently experimental, and we do not recommend it for production environments.
+
+{% include anchor.html title="Verify your installation" hash="verify-your–installation" %}
+
+Once CouchDB is installed, it should be running at `localhost:5984`. To verify, you can open up your terminal and type
+
+```bash
+$ curl localhost:5984
+```
+
+You should see something like:
+
+```js
+{"couchdb":"Welcome","version":"2.2.0",...}
+```
+
+Next, open up [http://localhost:5984/_utils/](http://localhost:5984/_utils/) in your browser.
+
+If you see a screen like the following, then you are ready to rock and roll with CouchDB:
+
+
+{% include img.html src="fauxton.png" alt="Fauxton interface" %}
+
+{% include anchor.html title="Set up CORS" hash="set-up-cors" %}
+
+[CORS](https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS) is a web technology that allows web sites to use resources from another domain. You will want to enable this in your CouchDB before continuing, because otherwise PouchDB will not work unless it's served from exactly the same domain as CouchDB.
+
+Enabling CORS is easy. Just install this handy script:
+
+```bash
+$ npm install -g add-cors-to-couchdb
+```
+
+And run it:
+
+```bash
+$ add-cors-to-couchdb
+```
+
+If you installed PouchDB Server, CORS is enabled by default, and this step is not necessary.
+
+{% include anchor.html title="Next" hash="next" %}
+
+Now that you have CouchDB installed, let's install PouchDB.
diff --git a/docs/version/8.0.0/guides/setup-pouchdb.md b/docs/version/8.0.0/guides/setup-pouchdb.md
new file mode 100644
index 0000000000..baea6a4551
--- /dev/null
+++ b/docs/version/8.0.0/guides/setup-pouchdb.md
@@ -0,0 +1,97 @@
+---
+index: 3
+layout: guide.html
+title: Setting up PouchDB
+sidebar: guides_nav.html
+---
+
+
+Installing PouchDB is easy. There are a few different ways to do it:
+
+{% include anchor.html title="Direct download" hash="direct-download" %}
+
+Download the latest **pouchdb-{{site.version}}.min.js** from the big green button above. Then in your `index.html`:
+
+```html
+
+```
+
+{% include anchor.html title="Bower" hash="bower" %}
+
+Run this on the command line:
+
+```bash
+$ bower install pouchdb
+```
+
+Then in your `index.html`:
+
+```html
+
+```
+
+{% include anchor.html title="npm" hash="npm" %}
+
+Run this on the command line:
+
+```bash
+$ npm install pouchdb
+```
+
+Then in your `index.html`:
+
+```html
+
+```
+
+{% include anchor.html title="jsdelivr CDN" hash="jsdelivr-cdn" %}
+
+Add this to your `index.html`:
+
+```html
+
+```
+
+{% include anchor.html title="Node.js" hash="nodejs" %}
+
+Run this on the command line:
+
+```bash
+$ npm install pouchdb
+```
+
+Then in your JavaScript:
+
+```js
+const PouchDB = require('pouchdb');
+```
+
+{% include anchor.html title="With TypeScript" hash="typescript" %}
+
+Run this on the command line:
+
+```bash
+$ npm install pouchdb @types/pouchdb
+```
+
+In your `tsconfig.json` activate `allowSyntheticDefaultImports`:
+
+```json
+{
+ "compilerOptions": {
+ "allowSyntheticDefaultImports": true
+ }
+}
+```
+
+Then in your TypeScript:
+
+```typescript
+import PouchDB from 'pouchdb';
+```
+
+You can install a plugin (provided there is a [type definition for it in npm](https://www.npmjs.com/search?q=scope:types%20pouchdb)), import it in the same way and then pass the imported name to `PouchDB.plugin()` method just as you would do in JavaScript.
+
+{% include anchor.html title="Next" hash="next" %}
+
+Now that you have PouchDB installed, let's start working with databases.
diff --git a/docs/version/8.0.0/guides/updating-deleting.md b/docs/version/8.0.0/guides/updating-deleting.md
new file mode 100644
index 0000000000..ba3b8b6c82
--- /dev/null
+++ b/docs/version/8.0.0/guides/updating-deleting.md
@@ -0,0 +1,145 @@
+---
+index: 7
+layout: guide.html
+nav: Updating/deleting documents
+title: Updating and deleting documents
+sidebar: guides_nav.html
+---
+
+As we saw in the past two chapters, working with PouchDB documents can be tricky, because you have to manage the revision identifier `_rev`.
+
+Now that we understand promises, though, there are few techniques we can use to make our code more elegant and readable.
+
+{% include anchor.html title="Creating a default document" hash="creating-a–default-document" %}
+
+Often in our code, we'll want to `get()` a document, and if it doesn't exist, we want to create some default.
+
+For instance, let's say we have a configuration object. We want to provide some reasonable defaults for our config:
+
+```js
+{
+ _id: 'config',
+ background: 'blue',
+ foreground: 'white',
+ sparkly: 'false'
+}
+```
+
+This is a pretty good default setting! So let's write the code to set it as our default.
+
+Thankfully, promises make this rather easy:
+
+```js
+db.get('config').catch(function (err) {
+ if (err.name === 'not_found') {
+ return {
+ _id: 'config',
+ background: 'blue',
+ foreground: 'white',
+ sparkly: 'false'
+ };
+ } else { // hm, some other error
+ throw err;
+ }
+}).then(function (configDoc) {
+ // sweet, here is our configDoc
+}).catch(function (err) {
+ // handle any errors
+});
+```
+
+This code is doing the following:
+
+* Try to `get()` a doc with `_id` equal to `'config'`
+* If it doesn't find it, return the default doc
+* Otherwise, you'll just get back the existing document
+
+You can see **[a live example](http://bl.ocks.org/nolanlawson/0a01d466b2d331cf7e25)** of this code.
+
+{% include anchor.html title="Why must we dance this dance?" hash="why-must-we-dance-this-dance" %}
+
+A common question from new PouchDB/CouchDB users is: why do we have to deal with `_rev` at all? Why can't I just `put()` the document without providing a `_rev`?
+
+The answer is: because `_rev`s are what makes sync work so well. PouchDB asks for a little upfront effort with managing document revisions, so that later on, sync is a breeze.
+
+In fact, you are probably already familiar with a system that forces you to go through a similar dance. This system is called [Git](http://www.git-scm.com/).
+
+PouchDB and CouchDB's document revision structure is very similar to Git's. In fact, each document's revision history is stored as a tree (exactly like Git), which allows you to handle conflicts when any two databases get out of sync.
+
+```
+rev 3-a rev 3-b
+ \___/
+ |
+ rev 2
+ |
+ rev 1
+```
+
+Conflicts will be discussed later in this guide. For now, you can think of revisions as being a single lineage:
+
+```
+ rev 4
+ |
+ rev 3
+ |
+ rev 2
+ |
+ rev 1
+```
+
+{% include anchor.html title="Deleting documents" hash="deleting-documents" %}
+
+When you `remove()` a document, it's not really deleted; it just gets a `_deleted` attribute added to it.
+
+That is, the database saves a tombstone at the end of the revision tree.
+
+```
+{_id: 'foo', _rev: '4-z', _deleted: true}
+ |
+{_id: 'foo', _rev: '3-y'}
+ |
+{_id: 'foo', _rev: '2-x'}
+ |
+{_id: 'foo', _rev: '1-w'}
+```
+
+There are three ways of deleting a document, which are all equivalent:
+
+1) You can call `db.remove(doc)`:
+
+```js
+db.get('mydoc').then(function (doc) {
+ return db.remove(doc);
+});
+```
+
+2) You can call `db.remove(doc._id, doc._rev)`:
+
+```js
+db.get('mydoc').then(function (doc) {
+ return db.remove(doc._id, doc._rev);
+});
+```
+
+3) You can call `db.put(doc)` with `_deleted` set to `true`:
+
+```js
+db.get('mydoc').then(function (doc) {
+ doc._deleted = true;
+ return db.put(doc);
+});
+```
+
+Of course, you will want to add `catch()` to the end of all these, unless you like to live dangerously.
+
+You can see **[a live example](http://bl.ocks.org/nolanlawson/b2049ad69308e92f15bc)** of this code.
+
+{% include anchor.html title="Related API documentation" hash="related-api-documentation" %}
+
+* [get()](/api.html#fetch_document)
+* [put()](/api.html#create_document)
+* [remove()](/api.html#delete_document)
+
+{% include anchor.html title="Next" hash="next" %}
+
+Now that we understand how to update and delete documents, let's do it in bulk.
diff --git a/docs/version/8.0.0/learn.md b/docs/version/8.0.0/learn.md
new file mode 100644
index 0000000000..d23ac36335
--- /dev/null
+++ b/docs/version/8.0.0/learn.md
@@ -0,0 +1,38 @@
+---
+layout: 2ColLeft.html
+title: About PouchDB
+sidebar: ./nav.html
+---
+
+PouchDB is an **in-browser database** that allows applications to save data locally, so that users can enjoy all the features of an app even when they're offline. Plus, the data is synchronized between clients, so users can stay up-to-date wherever they go.
+
+PouchDB also runs in **Node.js** and can be used as a direct interface to **CouchDB**-compatible servers. The API works the same in every environment, so you can spend less time worrying about browser differences, and more time writing clean, consistent code.
+
+PouchDB is a free open-source project, written in JavaScript and driven by our [wonderful community](https://github.com/apache/pouchdb/graphs/contributors). If you want to get involved, then check out the [contributing guide](https://github.com/apache/pouchdb/blob/master/CONTRIBUTING.md).
+
+{% include anchor.html class="h3" title="Browser Support" hash="browser_support" %}
+
+PouchDB supports all modern browsers, using [IndexedDB][] under the hood and falling back to [WebSQL][] where IndexedDB isn't supported. It is [fully tested](https://github.com/apache/pouchdb/actions) and supported in:
+
+ * Firefox 29+ (Including Firefox OS and Firefox for Android)
+ * Chrome 30+
+ * Safari 5+
+ * Internet Explorer 10+
+ * Opera 21+
+ * Android 4.0+
+ * iOS 7.1+
+ * Windows Phone 8+
+
+PouchDB also runs in [Cordova/PhoneGap](https://github.com/nolanlawson/pouchdb-phonegap-cordova), [NW.js](https://github.com/nolanlawson/pouchdb-nw), [Electron](https://github.com/nolanlawson/pouchdb-atom-shell), and [Chrome apps](https://github.com/nolanlawson/pouchdb-chrome-app). It is framework-agnostic, and you can use it with Angular, React, Ember, Backbone, or your framework of choice. There are [many adapters]({{ site.baseurl }}/external.html#framework_adapters), or you can just use PouchDB as-is.
+
+PouchDB requires a modern ES5 environment, so if you need to support older browsers (IE <10, Android <4.0, Opera Mini), then you should include the [es5-shim](https://github.com/es-shims/es5-shim) library. You can also use the [LocalStorage and in-memory adapters](/adapters.html#pouchdb_in_the_browser), or fall back to a live CouchDB.
+
+{% include anchor.html class="h3" title="Node.js" hash="node_js" %}
+
+In Node.js, PouchDB uses [LevelDB][] under the hood, and also supports [many other backends](/adapters.html#pouchdb_in_node_js) via the [LevelUP ecosystem](https://github.com/rvagg/node-levelup).
+
+PouchDB can also run as its own CouchDB-compatible web server, using [PouchDB Server](https://github.com/pouchdb/pouchdb-server).
+
+[IndexedDB]: http://caniuse.com/#feat=indexeddb
+[WebSQL]: http://caniuse.com/#feat=sql-storage
+[LevelDB]: https://github.com/google/leveldb
diff --git a/docs/version/8.0.0/offline.md b/docs/version/8.0.0/offline.md
new file mode 100644
index 0000000000..105fbc1a23
--- /dev/null
+++ b/docs/version/8.0.0/offline.md
@@ -0,0 +1,11 @@
+---
+layout: default.html
+title: Looks like you're offline...
+edit: false
+---
+
+
+
+
You need to be online to see this page, once you've seen it once it'll be available offline in the future!
+
+
diff --git a/docs/version/8.0.0/users.md b/docs/version/8.0.0/users.md
new file mode 100644
index 0000000000..b9aed7570a
--- /dev/null
+++ b/docs/version/8.0.0/users.md
@@ -0,0 +1,132 @@
+---
+layout: 2ColLeft.html
+title: Who's using PouchDB?
+sidebar: ./nav.html
+---
+
+A list of known products and services that are using PouchDB.
+## Newt
+
+{% include img.html width=100 src="newt.png" alt="Newt" %}
+
+[Newt](https://newt.to) lets you read, listen and write thousands of books across devices natively. It runs on Windows, Linux, macOS, Android, iOS & web. Its backed is powered by CouchDB and it uses PouchDB on the web client, desktop client and mobile native app for offline access, live sync, revisions and much more.
+
+## BikeCommute
+
+[BikeCommute](https://github.com/autonome/bikecommute) is a FirefoxOS app that registers an NFC tag to track bike commuters in the Mozilla Portland office. Built using Famo.us, PouchDB, and CouchDB. See [a video of it in action](https://youtu.be/3BVZYcQ-TYA) or [read about it on Mozilla Hacks](https://hacks.mozilla.org/2014/11/nfc-in-firefox-os/).
+
+## Cloudwall
+
+{% include img.html width=200 src="cloudwall.png" alt="Cloudwall" %}
+
+[Cloudwall](http://cloudwall.me/) is an operating system for noBackend webapps, based on CouchDB and PouchDB.
+
+## Cozy Cloud
+
+{% include img.html width=125 src="cozy.png" alt="Cozy Cloud" %}
+
+[Cozy](https://cozy.io/en/) is a personal cloud that you can host, customize, and fully control. It syncs contacts, calendars, and files between your personal devices and server. Under the hood, it leverages CouchDB and PouchDB.
+
+## Delta
+[Delta](https://github.com/octavore/delta) is a command-line utility for text diffs. View split diffs in the browser with syntax highlighting, or in the command-line using the --cli flag.
+
+## DevITJobs
+[DevITJobs](http://devitjobs.com) is a job board for the tech industry with mandatory salary ranges & tech stacks.
+
+## eHealth Africa
+
+{% include img.html width=200 src="ehealth_africa.png" alt="eHealth Africa" %}
+
+[eHealth Africa](http://ehealthafrica.org/) is an American-Nigerian NGO specialising in the development and deployment of tech for health. To tackle the Ebola outbreak, they built [mobile apps and dashboards](https://github.com/eHealthAfrica) to help track the spread of infection in the field. The combination of CouchDB and PouchDB enabled these apps to work consistently despite the extreme network unreliability of sub-saharan Africa.
+
+## Financier
+
+[Financier](https://financier.io) is a freemium personal budgeting app that uses PouchDB to store your budget data. The paid version syncs data with CouchDB 2 for data persistence and sharing across devices. The app is fully integrated with the PouchDB changes feed for instant updates. More about the stack is available in [humans.txt](https://app.financier.io/humans.txt).
+
+## GRADEpro GDT
+
+{% include img.html width=150 src="gradepro.png" alt="GRADEpro GDT" href="http://gradepro.org/" %}
+
+[GRADEpro GDT](http://gradepro.org/) is an easy to use, all‐in‐one web solution to summarize and present information for healthcare decision making. PouchDB is instrumental for both the collaboration features of GRADEpro (so many users can work on the same data simultaneously) and implementation of the offline mode. The tool is used in more than 135 countries, some of which have poor Internet connectivity, as well as by scientists, who are frequent travelers and need to use the application e.g. on a plane.
+
+## Hoodie
+
+{% include img.html width=150 src="HoodieLogo.png" alt="Hoodie" href="http://hood.ie/" %}
+
+[Hoodie](http://hood.ie/) provides a complete backend solution for your frontend code. It helps you develop your web application fast and easy. Hoodie-based apps are [offline-first](http://offlinefirst.org/) so they are usable anytime. Just plug Hoodie’s API into your frontend code, and your app is ready.
+
+## HospitalRun
+
+{% include img.html width=150 src="hospitalrun-logo.svg" alt="HospitalRun" href="http://hospitalrun.io/" %}
+
+[HospitalRun](http://hospitalrun.io/) is an open source software product designed specifically for developing world hospitals, making usability the key requirement. Using PouchDB and offline-first design, HospitalRun allows records to be carried to remote clinics, functioning when there is no Internet, and syncing when there is.
+
+## Local NPM
+
+[Local NPM](https://github.com/nolanlawson/local-npm) allows you to easily set up a local NPM server that caches data from the real NPM and updates in realtime. The goal is to cut down network request time by moving data closer to the client. Built using Node.js, LevelDB, and PouchDB.
+
+## Lullabot
+
+[Lullabot](https://www.lullabot.com) is an interactive strategy, design, and development company. We create delightful experiences using Drupal and open source technologies. Our main website is using Drupal as the back-end for content management, CouchDB with PouchDB for the API, and ReactJS for the front-end.
+
+## MBTA Alerts
+
+{% include img.html width=100 src="mbta_alerts.jpeg" alt="MBTA Alerts" %}
+
+[MBTA Alerts](https://twitter.com/MBTA_Alerts) is a Twitter bot that thousands of Bostonians depend upon to be notified of delays in the MBTA transit system. Uses Node.js, PouchDB, and CouchDB.
+
+## Medic Mobile
+
+{% include img.html src="medic-mobile.png" alt="Medic Mobile" %}
+
+[Medic Mobile](http://medic.org) is a software toolkit that combines smart messaging, decision support, easy data gathering and management, and health system analytics. Thanks to PouchDB our tools are offline first so workers can file reports wherever they are and sync when back online.
+
+## MoneyTracker.cc
+
+[MoneyTracker](https://moneytracker.cc/) is an open-source personal finances tracking web app. This app can work offline on desktop, tablet and mobile.
+Data is stored locally on device in PouchDB database and can be synced to the cloud.
+
+## NPM Browser
+
+[NPM Browser](https://github.com/pouchdb/npm-browser) is a fully offline cache of NPM packages, which runs in your browser, using Angular.js, PouchDB, and the pouchdb-load plugin.
+
+## Pokedex.org
+
+{% include img.html width=100 src="pokedexorg-logo.png" alt="Pokedex.org logo" %}
+
+[Pokedex.org](https://github.com/nolanlawson/pokedex.org) is a progressive web app, powered by ServiceWorker, PouchDB, virtual-dom, and web workers.
+
+## Quizster
+
+{% include img.html width=100 src="quizster.svg" alt="Quizster" href="https://quizster.co" %}
+
+[Quizster](https://quizster.co) is a photo-based submission and feedback system. Quizster uses PouchDB to allow real-time communication between students and teachers.
+
+## Squarespace Blog
+
+{% include img.html width=100 src="squarespace_blog.png" alt="Squarespace Blog" %}
+
+Squarespace Blog is an [Android](https://play.google.com/store/apps/details?id=com.squarespace.android.blog) and [iOS app](https://itunes.apple.com/us/app/squarespace-blog/id715084234) that gives you the tools you need to write and edit posts on multiple Squarespace websites. It uses PouchDB attachments inside a WebView for fast offline images.
+
+## Story-writer
+
+Story-writer is a free Markdown editor, with a [web version](http://markdown.xiaoshujiang.com) and [NW.js client version](http://soft.xiaoshujiang.com), built using Node.js, NW.js, LevelDB, and PouchDB.
+
+## Thali project
+
+[Thali](http://thaliproject.org/) is a Microsoft-sponsored open-source platform for creating apps that exploit the power of personal devices and put people in control of their data. It uses Cordova, PouchDB, OpenSSL, and Tor.
+
+## StudyMD
+
+{% include img.html width=100 src="studymd.png" alt="StudyMD" %}
+
+[StudyMD](https://github.com/jotron/StudyMD) transforms your markdown files into flashcards. It's an electronjs app based on reactjs. PouchDB is used in Node.js for storing the flashcards in a LevelDB database.
+
+## YLD
+
+[YLD](http://www.yld.io) is a Node.js software engineering, consulting and training company. We partner with enterprises to strengthen their software engineering culture and create the agility necessary to compete in today’s market, and are responsible for some of the largest Node.js solutions in production today. PouchDB allows us to create responsive, resilient and sync-enabled web applications.
+
+## FactoryTalk TeamONE
+
+{% include img.html src="TeamONE.png" alt="FactoryTalk TeamONE" %}
+[FactoryTalk® TeamONE™](http://33seconds.io) delivers one new experience and helps industrial teams improve productivity. Created by [Rockwell Automation](http://www.rockwellautomation.com/). We're using PouchDB for offline access and team synchronization. Looking to use the [Thali project](http://thaliproject.org/) (also a PouchDB client listed here) to enable true peer-to-peer connectivity. Connect with us on twitter ([@ROKTeamONE](https://twitter.com/rokteamone).)
diff --git a/docs/version/9.0.0/adapters.md b/docs/version/9.0.0/adapters.md
new file mode 100644
index 0000000000..cf09c5dd44
--- /dev/null
+++ b/docs/version/9.0.0/adapters.md
@@ -0,0 +1,220 @@
+---
+layout: 2ColLeft.html
+title: Adapters
+sidebar: ./nav.html
+---
+
+PouchDB is not a self-contained database; it is a CouchDB-style abstraction layer over other databases. By default, PouchDB ships with the [IndexedDB][] adapter for the browser, and a [LevelDB][] adapter in Node.js. This can be visualized as so:
+
+
+
+PouchDB attempts to provide a consistent API that "just works" across every browser and JavaScript environment, and in most cases, you can just use the defaults. However, if you're trying to reach the widest possible audience, or if you want the best performance, then you will sometimes want to tinker with the adapter settings.
+
+#### Topics:
+* [PouchDB in the browser](#pouchdb_in_the_browser)
+* [PouchDB in Node.js](#pouchdb_in_node_js)
+* [PouchDB over HTTP](#pouchdb_over_http)
+* [More resources](#more_resources)
+
+
+{% include anchor.html title="PouchDB in the browser" hash="pouchdb_in_the_browser"%}
+
+In the browser, PouchDB prefers IndexedDB.
+
+{% include alert/start.html variant="info"%}
+Prior to PouchDB 7.0.0, the WebSQL adapter was used for Safari/iOS. The WebSQL adapter no longer ships in PouchDB, but may be installed separately.
+{% include alert/end.html%}
+
+If you're ever curious which adapter is being used in a particular browser, you can use the following method:
+
+```js
+const pouch = new PouchDB('myDB');
+console.log(pouch.adapter); // prints 'idb'
+```
+
+### SQLite plugin for Cordova/PhoneGap
+
+On Cordova/PhoneGap/Ionic, the native SQLite database is often a popular choice, because it allows unlimited storage (compared to [IndexedDB/WebSQL storage limits](http://www.html5rocks.com/en/tutorials/offline/quota-research)). It also offers more flexibility in backing up and pre-loading databases, because the SQLite files are directly accessible to app developers.
+
+There are various Cordova plugins that can provide access to native SQLite, such as
+[Cordova-sqlite-storage](https://github.com/litehelpers/Cordova-sqlite-storage),
+[cordova-plugin-sqlite-2](https://github.com/nolanlawson/cordova-plugin-sqlite-2), or
+[cordova-plugin-websql](https://github.com/Microsoft/cordova-plugin-websql).
+
+To use them, you must install them separately into your Cordova application, and then add a special third-party PouchDB adapter
+called [pouchdb-adapter-cordova-sqlite](https://github.com/nolanlawson/pouchdb-adapter-cordova-sqlite). Once you do
+that, you can use it via:
+
+```js
+const db = new PouchDB('myDB.db', {adapter: 'cordova-sqlite'});
+```
+
+{% include alert/start.html variant="info"%}
+In PouchDB pre-6.0.0, Cordova SQLite support was available out-of-the-box, but it has been moved to a separate plugin
+to reduce confusion and to make it explicit whether you are using WebSQL or Cordova SQLite.
+{% include alert/end.html%}
+
+We recommend avoiding Cordova SQLite unless you are hitting the 50MB storage limit in iOS, you
+require native or preloaded access to the database files, or there's some other reason to go native.
+The built-in IndexedDB adapter is nearly always more performant and stable.
+
+### Browser adapter plugins
+
+PouchDB also offers separate browser plugins that use backends other than IndexedDB. These plugins fully pass the PouchDB test suite and are rigorously tested in our CI process.
+
+**Downloads:**
+
+* [pouchdb.memory.js](https://github.com/apache/pouchdb/releases/download/{{ site.version }}/pouchdb.memory.js) (Minified: [pouchdb.memory.min.js](https://github.com/apache/pouchdb/releases/download/{{ site.version }}/pouchdb.memory.min.js))
+* [pouchdb.localstorage.js](https://github.com/apache/pouchdb/releases/download/{{ site.version }}/pouchdb.localstorage.js) (Minified: [pouchdb.localstorage.min.js](https://github.com/apache/pouchdb/releases/download/{{ site.version }}/pouchdb.localstorage.min.js))
+
+{% include alert/start.html variant="warning"%}
+{% markdown %}
+These plugins add a hefty footprint due to external dependencies, so take them with a grain of salt.
+{% endmarkdown %}
+{% include alert/end.html%}
+
+#### In-memory adapter
+
+If you want a quick database for your unit tests, you can use the `pouchdb.memory.js` plugin, which offers a pure in-memory PouchDB:
+
+```html
+
+
+
+```
+
+This pouch will act exactly like a normal one – replicating, storing attachments, pagination, etc. – but it will be deleted as soon as the user closes their browser. However, multiple `PouchDB` objects with the same database name will share the same data:
+
+```js
+// pouch1 and pouch2 will share the same data
+const pouch1 = new PouchDB('myDB', {adapter: 'memory'});
+const pouch2 = new PouchDB('myDB', {adapter: 'memory'});
+
+// pouch3 will have its own data
+const pouch3 = new PouchDB('myOtherDB', {adapter: 'memory'});
+```
+
+#### LocalStorage adapter
+
+If you need to support very old browsers, such as IE ≤ 9.0 and Opera Mini, you can use the `pouchdb.localstorage.js` plugin, which allows PouchDB to fall back to [LocalStorage][] on browsers that don't support either IndexedDB or WebSQL. The [es5-shims][] will also be necessary.
+
+```html
+
+
+
+```
+
+{% include alert/start.html variant="warning"%}
+The LocalStorage plugin should be considered highly experimental, and the underlying structure may change in the future. Currently it stores all document IDs in memory, which works fine on small databases but may crash on larger databases. You can follow localstorage-down to track our progress.
+{% include alert/end.html %}
+
+{% include anchor.html title="PouchDB in Node.js" hash="pouchdb_in_node_js"%}
+
+#### In-memory
+
+{% include alert/start.html variant="warning"%}
+{% markdown %}
+**Warning: deprecation notice.** The `memory` adapter will be deprecated in PouchDB version 10.0.0 and removed in version 11.0.0. You can read [the migration guide here](https://pouchdb.com/2026/04/10/migration-to-nodesqlite.html) and more about the topic in [this link](https://github.com/apache/pouchdb/issues/9163).
+{% endmarkdown %}
+{% include alert/end.html%}
+
+Just as in the browser, you can also create a pure in-memory PouchDB:
+
+```bash
+$ npm install pouchdb-adapter-memory
+```
+
+then:
+
+```js
+PouchDB.plugin(require('pouchdb-adapter-memory'));
+const pouch = new PouchDB('myDB', {adapter: 'memory'});
+```
+
+This implementation is based on [MemDOWN](https://github.com/level/memdown), and will not write any changes to disk.
+
+#### Node SQLite adapter
+
+You can also use PouchDB in Node.js' [native SQLite module](https://nodejs.org/api/sqlite.html), when using Node.js' `>22.5.0` version.
+
+```js
+const PouchDB = require('pouchdb');
+PouchDB.plugin(require('pouchdb-adapter-node-sqlite'));
+
+const db = new PouchDB('mydatabase.db', {adapter: 'nodesqlite'});
+```
+
+#### Other LevelDOWN adapters
+
+{% include alert/start.html variant="warning"%}
+{% markdown %}
+**Warning: deprecation notice.** The `leveldb` adapter will be deprecated in PouchDB version 10.0.0 and removed in version 11.0.0. You can read [the migration guide here](https://pouchdb.com/2026/04/10/migration-to-nodesqlite.html) and more about the topic in [this link](https://github.com/apache/pouchdb/issues/9163).
+{% endmarkdown %}
+{% include alert/end.html%}
+
+Technically you are free to use
+[any LevelDOWN-based implementation](https://github.com/rvagg/node-levelup/wiki/Modules#storage-back-ends) in either Node or the browser.
+However this should be considered **extremely experimental** and not designed for production use.
+
+See [pouchdb-adapter-leveldb-core](https://www.npmjs.com/package/pouchdb-adapter-leveldb-core) for details.
+
+{% include anchor.html title="PouchDB over HTTP" hash="pouchdb_over_http"%}
+
+In both the browser and in Node.js, PouchDB can also function as a straightforward API on top of any [CouchDB](https://couchdb.apache.org/)-compliant database:
+
+```js
+const pouch = new PouchDB('http://my-site.com:5984/my-db');
+const securePouch = new PouchDB('https://my-secure-site.com:5984/my-secure-db');
+```
+
+You can also sync to and from these databases to your local PouchDB.
+
+Currently PouchDB has full support for:
+
+* CouchDB 1.x
+* [Smileupps](https://www.smileupps.com/) (same as 1.x)
+* CouchDB 2.x ([tested in CI](https://github.com/apache/pouchdb/actions))
+* CouchDB 3.x ([tested in CI](https://github.com/apache/pouchdb/actions))
+* [Cloudant](https://cloudant.com/) (roughly the same as 2.x)
+* [PouchDB Server](https://github.com/pouchdb/pouchdb-server) ([tested in CI](https://github.com/apache/pouchdb/actions))
+* [PouchDB Server --in-memory mode](https://github.com/pouchdb/pouchdb-server)
+
+[Drupal 8](http://wearepropeople.com/blog/a-content-staging-solution-for-drupal-8-and-more) has also announced support for PouchDB, and there is [rcouch](https://github.com/rcouch/rcouch) as well, but these are both untested by PouchDB.
+
+If you are ever unsure about a server, consider replicating from PouchDB to CouchDB, then from that CouchDB to the other server.
+
+#### PouchDB Server
+
+[PouchDB Server](https://github.com/pouchdb/pouchdb-server) is a standalone REST server that implements the CouchDB API, while using a LevelDB-based PouchDB under the hood. It also supports an `--in-memory` mode and any [LevelDOWN][] adapter, which you may find handy.
+
+PouchDB Server passes the PouchDB test suite at 100%, but be aware that it is not as full-featured or battle-tested as CouchDB.
+
+#### PouchDB Express
+
+The underlying module for PouchDB Server, [Express PouchDB](https://github.com/pouchdb/express-pouchdb) is an Express submodule that mimics most of the CouchDB API within your Express application.
+
+{% include anchor.html title="More resources" hash="more_resources"%}
+
+The best place to look for information on which browsers support which databases is [caniuse.com](http://caniuse.com). You can consult their tables on browser support for various backends:
+
+* [IndexedDB](http://caniuse.com/indexeddb)
+* [WebSQL](http://caniuse.com/sql-storage)
+* [LocalStorage](http://caniuse.com/namevalue-storage)
+
+[IndexedDB]: http://www.w3.org/TR/IndexedDB/
+[WebSQL]: http://www.w3.org/TR/webdatabase/
+[LevelDB]: https://code.google.com/p/leveldb/
+[LocalStorage]: https://developer.mozilla.org/en-US/docs/Web/Guide/API/DOM/Storage
+[es5-shims]: https://github.com/es-shims/es5-shim
+[sqlite plugin]: https://github.com/brodysoft/Cordova-SQLitePlugin
+[sqlite plugin 2]: https://github.com/nolanlawson/cordova-plugin-sqlite-2
+[leveldown]: https://github.com/rvagg/node-leveldown
+[level-js]: https://github.com/maxogden/level.js
diff --git a/docs/version/9.0.0/api.html b/docs/version/9.0.0/api.html
new file mode 100644
index 0000000000..e7d2e9d777
--- /dev/null
+++ b/docs/version/9.0.0/api.html
@@ -0,0 +1,42 @@
+---
+layout: 2ColLeft.html
+title: API Reference
+sidebar: api.html
+edit: false
+---
+
+{% markdown %}
+
+{% include ./api/overview.html %}
+{% include ./api/create_database.html %}
+{% include ./api/delete_database.html %}
+{% include ./api/create_document.html %}
+{% include ./api/fetch_document.html %}
+{% include ./api/delete_document.html %}
+{% include ./api/batch_create.html %}
+{% include ./api/batch_fetch.html %}
+{% include ./api/changes.html %}
+{% include ./api/replication.html %}
+{% include ./api/sync.html %}
+{% include ./api/save_attachment.html %}
+{% include ./api/get_attachment.html %}
+{% include ./api/delete_attachment.html %}
+{% include ./api/create_index.html %}
+{% include ./api/query_index.html %}
+{% include ./api/explain_index.html %}
+{% include ./api/list_indexes.html %}
+{% include ./api/delete_index.html %}
+{% include ./api/query_database.html %}
+{% include ./api/view_cleanup.html %}
+{% include ./api/database_information.html %}
+{% include ./api/compaction.html %}
+{% include ./api/revisions_diff.html %}
+{% include ./api/bulk_get.html %}
+{% include ./api/close_database.html %}
+{% include ./api/purge.html %}
+{% include ./api/events.html %}
+{% include ./api/active_tasks.html %}
+{% include ./api/defaults.html %}
+{% include ./api/plugins.html %}
+
+{% endmarkdown %}
diff --git a/docs/version/9.0.0/api/active_tasks.html b/docs/version/9.0.0/api/active_tasks.html
new file mode 100644
index 0000000000..673a8a8850
--- /dev/null
+++ b/docs/version/9.0.0/api/active_tasks.html
@@ -0,0 +1,45 @@
+{% include anchor.html edit="true" title="List active tasks" hash="active_tasks" %}
+
+{% highlight js %}
+PouchDB.activeTasks.list()
+{% endhighlight %}
+
+List all active database tasks. There are three types of internal tasks: `database_compaction`, `view_indexing`, and `replication`. PouchDB will report progress of these tasks to the active tasks API and remove tasks as soon as they are completed or have failed.
+
+#### Example Usage:
+
+{% highlight js %}
+const tasks = PouchDB.activeTasks.list()
+{% endhighlight %}
+
+#### Example Result:
+
+{% highlight js %}
+[{
+ "id": "d81fea92-8ce4-42df-bb2b-89a4e67536c3",
+ "name": "database_compaction",
+ "created_at": "2022-02-08T15:38:45.318Z",
+ "total_items": 12,
+ "completed_items": 1,
+ "updated_at": "2022-02-08T15:38:45.821Z"
+}]
+{% endhighlight %}
+
+### Real-time updates
+
+You can use [JavaScript Proxies](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy) to monitor calls to the active tasks API. For the `PouchDB.activeTasks.add()` function, which is used internally to announce new tasks to PouchDB, you can monitor calls as follows:
+
+{% highlight js %}
+PouchDB.activeTasks.add = new Proxy(PouchDB.activeTasks.add, {
+ apply: (target, thisArg, argumentsList) => {
+ const task = argumentsList[0];
+ const id = Reflect.apply(
+ target,
+ PouchDB.activeTasks,
+ [task]
+ );
+ console.log('Added task', id, task.name);
+ return id;
+ },
+});
+{% endhighlight %}
diff --git a/docs/version/9.0.0/api/batch_create.html b/docs/version/9.0.0/api/batch_create.html
new file mode 100644
index 0000000000..13f58a6abf
--- /dev/null
+++ b/docs/version/9.0.0/api/batch_create.html
@@ -0,0 +1,271 @@
+{% include anchor.html edit="true" title="Create/update a batch of documents" hash="batch_create" %}
+
+{% highlight js %}
+db.bulkDocs(docs, [options], [callback])
+{% endhighlight %}
+
+Create, update or delete multiple documents. The `docs` argument is an array of documents.
+
+If you omit an `_id` parameter on a given document, the database will create a new document and assign the ID for you. To update a document, you must include both an `_id` parameter and a `_rev` parameter, which should match the ID and revision of the document on which to base your updates. Finally, to delete a document, include a `_deleted` parameter with the value `true`.
+
+#### Example Usage:
+
+Put some new docs, providing the `_id`s:
+
+{% include code/start.html id="bulk_docs_1" type="callback" %}
+{% highlight js %}
+db.bulkDocs([
+ {title : 'Lisa Says', _id: 'doc1'},
+ {title : 'Space Oddity', _id: 'doc2'}
+], function(err, response) {
+ if (err) { return console.log(err); }
+ // handle result
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="bulk_docs_1" type="async" %}
+{% highlight js %}
+try {
+ const result = await db.bulkDocs([
+ {title : 'Lisa Says', _id: 'doc1'},
+ {title : 'Space Oddity', _id: 'doc2'}
+ ]);
+} catch (err) {
+ console.log(err);
+}
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="bulk_docs_1" type="promise" %}
+{% highlight js %}
+db.bulkDocs([
+ {title : 'Lisa Says', _id: 'doc1'},
+ {title : 'Space Oddity', _id: 'doc2'}
+]).then(function (result) {
+ // handle result
+}).catch(function (err) {
+ console.log(err);
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+Post some new docs and auto-generate the `_id`s:
+
+{% include code/start.html id="bulk_docs_2" type="callback" %}
+{% highlight js %}
+db.bulkDocs([
+ {title : 'Lisa Says'},
+ {title : 'Space Oddity'}
+], function(err, response) {
+ if (err) { return console.log(err); }
+ // handle result
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="bulk_docs_2" type="async" %}
+{% highlight js %}
+try {
+ const result = await db.bulkDocs([
+ {title : 'Lisa Says'},
+ {title : 'Space Oddity'}
+ ]);
+} catch (err) {
+ console.log(err);
+}
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="bulk_docs_2" type="promise" %}
+{% highlight js %}
+db.bulkDocs([
+ {title : 'Lisa Says'},
+ {title : 'Space Oddity'}
+]).then(function (result) {
+ // handle result
+}).catch(function (err) {
+ console.log(err);
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+#### Example Response:
+{% highlight js %}
+[
+ {
+ "ok": true,
+ "id": "doc1",
+ "rev": "1-84abc2a942007bee7cf55007cba56198"
+ },
+ {
+ "ok": true,
+ "id": "doc2",
+ "rev": "1-7b80fc50b6af7a905f368670429a757e"
+ }
+]
+{% endhighlight %}
+
+The response contains an array of the familiar `ok`/`rev`/`id`
+from the [put()/post() API](#create_document). If there are any errors, they
+will be provided individually like so:
+
+{% highlight js %}
+[
+ { status: 409,
+ name: 'conflict',
+ message: 'Document update conflict',
+ error: true
+ }
+]
+{% endhighlight %}
+
+The results are returned in the same order as the supplied "docs" array.
+
+Note that `bulkDocs()` is not transactional, and that you may get
+back a mixed array of errors/non-errors. In CouchDB/PouchDB, the smallest
+atomic unit is the document.
+
+#### Bulk update/delete:
+
+You can also use `bulkDocs()` to update/delete many documents at once:
+
+{% include code/start.html id="bulk_docs3" type="callback" %}
+{% highlight js %}
+db.bulkDocs([
+ {
+ title : 'Lisa Says',
+ artist : 'Velvet Underground',
+ _id : "doc1",
+ _rev : "1-84abc2a942007bee7cf55007cba56198"
+ },
+ {
+ title : 'Space Oddity',
+ artist : 'David Bowie',
+ _id : "doc2",
+ _rev : "1-7b80fc50b6af7a905f368670429a757e"
+ }
+], function(err, response) {
+ if (err) { return console.log(err); }
+ // handle result
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="bulk_docs3" type="async" %}
+{% highlight js %}
+try {
+ const result = await db.bulkDocs([
+ {
+ title : 'Lisa Says',
+ artist : 'Velvet Underground',
+ _id : "doc1",
+ _rev : "1-84abc2a942007bee7cf55007cba56198"
+ },
+ {
+ title : 'Space Oddity',
+ artist : 'David Bowie',
+ _id : "doc2",
+ _rev : "1-7b80fc50b6af7a905f368670429a757e"
+ }
+ ]);
+} catch (err) {
+ console.log(err);
+}
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="bulk_docs3" type="promise" %}
+{% highlight js %}
+db.bulkDocs([
+ {
+ title : 'Lisa Says',
+ artist : 'Velvet Underground',
+ _id : "doc1",
+ _rev : "1-84abc2a942007bee7cf55007cba56198"
+ },
+ {
+ title : 'Space Oddity',
+ artist : 'David Bowie',
+ _id : "doc2",
+ _rev : "1-7b80fc50b6af7a905f368670429a757e"
+ }
+]).then(function (result) {
+ // handle result
+}).catch(function (err) {
+ console.log(err);
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+Or delete them:
+
+{% include code/start.html id="bulk_docs_4" type="callback" %}
+{% highlight js %}
+db.bulkDocs([
+ {
+ title : 'Lisa Says',
+ _deleted : true,
+ _id : "doc1",
+ _rev : "1-84abc2a942007bee7cf55007cba56198"
+ },
+ {
+ title : 'Space Oddity',
+ _deleted : true,
+ _id : "doc2",
+ _rev : "1-7b80fc50b6af7a905f368670429a757e"
+ }
+], function(err, response) {
+ if (err) { return console.log(err); }
+ // handle result
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="bulk_docs_4" type="async" %}
+{% highlight js %}
+try {
+ const result = await db.bulkDocs([
+ {
+ title : 'Lisa Says',
+ _deleted : true,
+ _id : "doc1",
+ _rev : "1-84abc2a942007bee7cf55007cba56198"
+ },
+ {
+ title : 'Space Oddity',
+ _deleted : true,
+ _id : "doc2",
+ _rev : "1-7b80fc50b6af7a905f368670429a757e"
+ }
+ ]);
+} catch (err) {
+ console.log(err);
+}
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="bulk_docs_4" type="promise" %}
+{% highlight js %}
+db.bulkDocs([
+ {
+ title : 'Lisa Says',
+ _deleted : true,
+ _id : "doc1",
+ _rev : "1-84abc2a942007bee7cf55007cba56198"
+ },
+ {
+ title : 'Space Oddity',
+ _deleted : true,
+ _id : "doc2",
+ _rev : "1-7b80fc50b6af7a905f368670429a757e"
+ }
+]).then(function (result) {
+ // handle result
+}).catch(function (err) {
+ console.log(err);
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+**Note:** If you set a `new_edits` property on the options object to `false`, you can post existing documents from other databases without having new revision IDs assigned to them. Normally, only the [replication algorithm](https://docs.couchdb.org/en/stable/replication/protocol.html?highlight=new_edits#upload-batch-of-changed-documents) needs to do this.
diff --git a/docs/version/9.0.0/api/batch_fetch.html b/docs/version/9.0.0/api/batch_fetch.html
new file mode 100644
index 0000000000..0e7e6749ae
--- /dev/null
+++ b/docs/version/9.0.0/api/batch_fetch.html
@@ -0,0 +1,205 @@
+{% include anchor.html edit="true" title="Fetch a batch of documents" hash="batch_fetch" %}
+
+{% highlight js %}
+db.allDocs([options], [callback])
+{% endhighlight %}
+
+Fetch multiple documents, indexed and sorted by the `_id`. Deleted documents are only included if `options.keys` is specified.
+
+### Options
+
+All options default to `false` unless otherwise specified.
+
+* `options.include_docs`: Include the document itself in each row in the `doc` field. Otherwise by default you only get the `_id` and `_rev` properties.
+* `options.conflicts`: Include conflict information in the `_conflicts` field of a doc.
+* `options.attachments`: Include attachment data as base64-encoded string.
+* `options.binary`: Return attachment data as Blobs/Buffers, instead of as base64-encoded strings.
+* `options.startkey` & `options.endkey`: Get documents with IDs in a certain range (inclusive/inclusive).
+* `options.inclusive_end`: Include documents having an ID equal to the given `options.endkey`. Default: `true`.
+* `options.limit`: Maximum number of documents to return.
+* `options.skip`: Number of docs to skip before returning (warning: poor performance on IndexedDB/LevelDB!).
+* `options.descending`: Reverse the order of the output documents. Note that the order of `startkey` and `endkey` is reversed when `descending`:`true`.
+* `options.key`: Only return documents with IDs matching this string key.
+* `options.keys`: Array of string keys to fetch in a single shot.
+ - Neither `startkey` nor `endkey` can be specified with this option.
+ - The rows are returned in the same order as the supplied `keys` array.
+ - The row for a deleted document will have the revision ID of the deletion, and an extra key `"deleted":true` in the `value` property.
+ - The row for a nonexistent document will just contain an `"error"` property with the value `"not_found"`.
+ - For details, see the [CouchDB query options documentation](https://docs.couchdb.org/en/stable/api/ddoc/views.html#db-design-design-doc-view-view-name).
+* `options.update_seq`: Include an `update_seq` value indicating which sequence id of the underlying database the view reflects.
+
+**Notes:** For pagination, `options.limit` and `options.skip` are also available, but the same performance concerns as in CouchDB apply. Use the [startkey/endkey pattern](https://docs.couchdb.org/en/stable/couchapp/views/pagination.html) instead.
+
+#### Example Usage:
+
+{% include code/start.html id="all_docs" type="callback" %}
+{% highlight js %}
+db.allDocs({
+ include_docs: true,
+ attachments: true
+}, function(err, response) {
+ if (err) { return console.log(err); }
+ // handle result
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="all_docs" type="async" %}
+{% highlight js %}
+try {
+ const result = await db.allDocs({
+ include_docs: true,
+ attachments: true
+ });
+} catch (err) {
+ console.log(err);
+}
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="all_docs" type="promise" %}
+{% highlight js %}
+db.allDocs({
+ include_docs: true,
+ attachments: true
+}).then(function (result) {
+ // handle result
+}).catch(function (err) {
+ console.log(err);
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+#### Example Response:
+
+{% highlight js %}
+{
+ "offset": 0,
+ "total_rows": 1,
+ "rows": [{
+ "doc": {
+ "_id": "0B3358C1-BA4B-4186-8795-9024203EB7DD",
+ "_rev": "1-5782E71F1E4BF698FA3793D9D5A96393",
+ "title": "Sound and Vision",
+ "_attachments": {
+ "attachment/its-id": {
+ "content_type": "image/jpg",
+ "data": "R0lGODlhAQABAIAAAP7//wAAACH5BAAAAAAALAAAAAABAAEAAAICRAEAOw==",
+ "digest": "md5-57e396baedfe1a034590339082b9abce"
+ }
+ }
+ },
+ "id": "0B3358C1-BA4B-4186-8795-9024203EB7DD",
+ "key": "0B3358C1-BA4B-4186-8795-9024203EB7DD",
+ "value": {
+ "rev": "1-5782E71F1E4BF698FA3793D9D5A96393"
+ }
+ }]
+}
+{% endhighlight %}
+
+In the response, you have three things:
+
+* `total_rows` the total number of non-deleted documents in the database
+* `offset` the `skip` if provided, or in CouchDB the actual offset
+* `rows`: rows containing the documents, or just the `_id`/`_revs` if you didn't set `include_docs` to `true`.
+
+* You may optionally also have `update_seq` if you set `update_seq` to `true`
+
+You can use `startkey`/`endkey` to find all docs in a range:
+
+{% include code/start.html id="all_docs_2" type="callback" %}
+{% highlight js %}
+db.allDocs({
+ include_docs: true,
+ attachments: true,
+ startkey: 'bar',
+ endkey: 'quux'
+}, function(err, response) {
+ if (err) { return console.log(err); }
+ // handle result
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="all_docs_2" type="async" %}
+{% highlight js %}
+try {
+ const result = await db.allDocs({
+ include_docs: true,
+ attachments: true,
+ startkey: 'bar',
+ endkey: 'quux'
+ });
+} catch (err) {
+ console.log(err);
+}
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="all_docs_2" type="promise" %}
+{% highlight js %}
+db.allDocs({
+ include_docs: true,
+ attachments: true,
+ startkey: 'bar',
+ endkey: 'quux'
+}).then(function (result) {
+ // handle result
+}).catch(function (err) {
+ console.log(err);
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+This will return all docs with `_id`s between `'bar'` and `'quux'`.
+
+#### Prefix search
+
+You can do prefix search in `allDocs()` – i.e. "give me all the documents whose `_id`s start with `'foo'`" – by using the special high Unicode character `'\ufff0'`:
+
+{% include code/start.html id="all_docs_3" type="callback" %}
+{% highlight js %}
+db.allDocs({
+ include_docs: true,
+ attachments: true,
+ startkey: 'foo',
+ endkey: 'foo\ufff0'
+}, function(err, response) {
+ if (err) { return console.log(err); }
+ // handle result
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="all_docs_3" type="async" %}
+{% highlight js %}
+try {
+ const result = await db.allDocs({
+ include_docs: true,
+ attachments: true,
+ startkey: 'foo',
+ endkey: 'foo\ufff0'
+ });
+} catch (err) {
+ console.log(err);
+}
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="all_docs_3" type="promise" %}
+{% highlight js %}
+db.allDocs({
+ include_docs: true,
+ attachments: true,
+ startkey: 'foo',
+ endkey: 'foo\ufff0'
+}).then(function (result) {
+ // handle result
+}).catch(function (err) {
+ console.log(err);
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+This works because CouchDB/PouchDB `_id`s are sorted [lexicographically](https://docs.couchdb.org/en/stable/couchapp/views/collation.html).
diff --git a/docs/version/9.0.0/api/bulk_get.html b/docs/version/9.0.0/api/bulk_get.html
new file mode 100644
index 0000000000..a664a3e1b3
--- /dev/null
+++ b/docs/version/9.0.0/api/bulk_get.html
@@ -0,0 +1,118 @@
+{% include anchor.html edit="true" title="Document bulk get" hash="bulk_get" %}
+
+{% highlight js %}
+db.bulkGet(options, [callback])
+{% endhighlight %}
+
+Given a set of document/revision IDs, returns the document bodies (and, optionally, attachment data) for each ID/revision pair specified.
+
+### Options
+
+* `options.docs`: An array of `id` and `rev` pairs representing the revisions to fetch.
+ - `id`: ID of the document to fetch.
+ - `rev`: Revision of the document to fetch. If this is not specified, all available revisions are fetched.
+ - `atts_since`: Optional and supported by the http adapter only. Includes attachments only since specified revisions. Doesn’t includes attachments for specified revisions.
+* `options.revs`: Each returned revision body will include its revision history as a `_revisions` property. Default is `false`.
+* `options.attachments`: Include attachment data in the response. Default is `false`, resulting in only stubs being returned.
+* `options.binary`: Return attachment data as Blobs/Buffers, instead of as base64-encoded strings. Default is `false`.
+
+
+
+#### Example Usage:
+
+{% include code/start.html id="bulkget1" type="callback" %}
+{% highlight js %}
+db.bulkGet({
+ docs: [
+ { id: "existing-doc", rev: "1-b2e54331db828310f3c772d6e042ac9c"},
+ { id: "foo", rev: "2-3a24009a9525bde9e4bfa8a99046b00d"},
+ { id: "bar", rev: "1-3a24009a9525bde9e4bfa8a99046b00d"}
+ ]
+}, function (err, result) {
+ if (err) { return console.log(err); }
+ // handle result
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="bulkget1" type="async" %}
+{% highlight js %}
+try {
+ const result = await db.bulkGet({
+ docs: [
+ { id: "doc-that-exists", rev: "1-967a00dff5e02add41819138abb3284d"},
+ { id: "doc-that-does-not-exist", rev: "1-3a24009a9525bde9e4bfa8a99046b00d"},
+ { id: "doc-that-exists", rev: "1-bad_rev"}
+ ]
+ });
+} catch (err) {
+ console.log(err);
+}
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="bulkget1" type="promise" %}
+{% highlight js %}
+db.bulkGet({
+ docs: [
+ { id: "doc-that-exists", rev: "1-967a00dff5e02add41819138abb3284d"},
+ { id: "doc-that-does-not-exist", rev: "1-3a24009a9525bde9e4bfa8a99046b00d"},
+ { id: "doc-that-exists", rev: "1-bad_rev"}
+ ]
+}).then(function (result) {
+ // handle result
+}).catch(function (err) {
+ console.log(err);
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+#### Example Response:
+{% highlight js %}
+{
+ "results": [
+ {
+ "docs": [
+ {
+ "ok": {
+ "_id": "doc-that-exists",
+ "_rev": "1-967a00dff5e02add41819138abb3284d",
+ "_revisions": {
+ "ids": [
+ "967a00dff5e02add41819138abb3284d"
+ ],
+ "start": 1
+ }
+ }
+ }],
+ "id": "doc-that-exists"
+ },
+ {
+ "docs": [
+ {
+ "error": {
+ "error": "not_found",
+ "id": "doc-that-does-not-exist",
+ "reason": "missing",
+ "rev": "undefined"
+ }
+ }],
+ "id": "doc-that-does-not-exist"
+ },
+ {
+ "docs": [
+ {
+ "error": {
+ "error": "not_found",
+ "id": "doc-that-exists",
+ "reason": "missing",
+ "rev": "1-badrev"
+ }
+ }
+ ],
+ "id": "doc-that-exists"
+ }]
+}
+{% endhighlight %}
+
+
diff --git a/docs/version/9.0.0/api/changes.html b/docs/version/9.0.0/api/changes.html
new file mode 100644
index 0000000000..6bfbf69079
--- /dev/null
+++ b/docs/version/9.0.0/api/changes.html
@@ -0,0 +1,358 @@
+{% include anchor.html edit="true" title="Listen to database changes" hash="changes" %}
+
+{% highlight js %}
+db.changes(options)
+{% endhighlight %}
+
+A list of changes made to documents in the database, in the order they were made.
+It returns an object with the method `cancel()`, which you call if you don't want to listen to new changes anymore.
+
+It is an [event emitter][event emitter] and will emit a `'change'` event on each document change, a `'complete'` event when all the changes have been processed, and an `'error'` event when an error occurs. Calling `cancel()` will unsubscribe all event listeners automatically.
+
+### Options
+
+All options default to `false` unless otherwise specified.
+
+* `options.live`: Will emit change events for all future changes until cancelled.
+* `options.include_docs`: Include the associated document with each change.
+ * `options.conflicts`: Include conflicts.
+ * `options.attachments`: Include attachments.
+ - `options.binary`: Return attachment data as Blobs/Buffers, instead of as base64-encoded strings.
+* `options.descending`: Reverse the order of the output documents.
+* `options.since`: Start the results from the change immediately after the given sequence number. You can also pass `'now'` if you want only new changes (when `live` is `true`). Ignored if `descending` is true.
+* `options.limit`: Limit the number of results to this number.
+* `options.timeout`: Request timeout (in milliseconds), use `false` to disable.
+* `options.heartbeat`: For http adapter only, time in milliseconds for server to give a heartbeat to keep long connections open. Defaults to 10000 (10 seconds), use `false` to disable the default.
+
+**Filtering Options:**
+
+* `options.filter`: Reference a filter function from a design document to selectively get updates. To use a view function, pass `_view` here and provide a reference to the view function in `options.view`. See [filtered changes](#filtered-changes) for details.
+* `options.doc_ids`: Only show changes for docs with these ids (array of strings).
+* `options.query_params`: Object containing properties that are passed to the filter function, e.g. `{"foo:"bar"}`, where `"bar"` will be available in the filter function as `params.query.foo`. To access the `params`, define your filter function like `function (doc, params) {/* ... */}`.
+* `options.view`: Specify a view function (e.g. `'design_doc_name/view_name'` or `'view_name'` as shorthand for `'view_name/view_name'`) to act as a filter. Documents counted as "passed" for a view filter if a map function emits at least one record for them. **Note**: `options.filter` must be set to `'_view'` for this option to work.
+* `options.selector`: Filter using a query/pouchdb-find [selector](https://docs.couchdb.org/en/stable/api/database/find.html#find-selectors). **Note**: Cannot be used in combination with the filter option.
+
+**Advanced Options:**
+
+* `options.return_docs`: Defaults to `true` except when `options.live = true` then it defaults to `false`. Passing `false` prevents the changes feed from keeping all the documents in memory – in other words complete always has an empty results array, and the `change` event is the only way to get the event. Useful for large change sets where otherwise you would run out of memory.
+* `options.batch_size`: Only available for http databases, this configures how many changes to fetch at a time. Increasing this can reduce the number of requests made. Default is 25.
+* `options.style`: Specifies how many revisions are returned in the changes array. The default, `'main_only'`, will only return the current "winning" revision; `'all_docs'` will return all leaf revisions (including conflicts and deleted former conflicts). Most likely you won't need this unless you're writing a replicator.
+* `options.seq_interval`: Only available for http databases. Specifies that seq information only be generated every N changes. Larger values can improve changes throughput with CouchDB 2.0 and later. Note that `last_seq` is always populated regardless.
+
+#### Example Usage:
+
+{% highlight js %}
+const changes = db.changes({
+ since: 'now',
+ live: true,
+ include_docs: true
+}).on('change', function(change) {
+ // handle change
+}).on('complete', function(info) {
+ // changes() was canceled
+}).on('error', function (err) {
+ console.log(err);
+});
+
+changes.cancel(); // whenever you want to cancel
+{% endhighlight %}
+
+#### Example Response:
+{% highlight js %}
+{
+ "id":"somestuff",
+ "seq":21,
+ "changes":[{
+ "rev":"1-8e6e4c0beac3ec54b27d1df75c7183a8"
+ }],
+ "doc":{
+ "title":"Ch-Ch-Ch-Ch-Changes",
+ "_id":"someDocId",
+ "_rev":"1-8e6e4c0beac3ec54b27d1df75c7183a8"
+ }
+}
+{% endhighlight %}
+
+#### Change events
+
+* __`change`__ (`info`) - This event fires when a change has been found. `info` will contain details about the change, such as whether it was deleted and what the new `_rev` is. `info.doc` will contain the doc if you set `include_docs` to `true`. See below for an example response.
+* __`complete`__ (`info`) - This event fires when all changes have been read. In live changes, only cancelling the changes should trigger this event. `info.results` will contain the list of changes. See below for an example.
+* __`error`__ (`err`) - This event is fired when the changes feed is stopped due to an unrecoverable failure.
+
+#### Example response
+
+Example response in the `'change'` listener (using `{include_docs: true}`):
+
+{% highlight js %}
+{ id: 'doc1',
+ changes: [ { rev: '1-9152679630cc461b9477792d93b83eae' } ],
+ doc: {
+ _id: 'doc1',
+ _rev: '1-9152679630cc461b9477792d93b83eae'
+ },
+ seq: 1
+}
+{% endhighlight %}
+
+Example response in the `'change'` listener when a doc was deleted:
+
+{% highlight js %}
+{ id: 'doc2',
+ changes: [ { rev: '2-9b50a4b63008378e8d0718a9ad05c7af' } ],
+ doc: { _id: 'doc2',
+ _rev: '2-9b50a4b63008378e8d0718a9ad05c7af',
+ _deleted: true
+ },
+ deleted: true,
+ seq: 3
+}
+{% endhighlight %}
+
+Example response in the `'complete'` listener:
+
+{% highlight js %}
+{
+ "results": [
+ {
+ "id": "doc1",
+ "changes": [ { "rev": "1-9152679630cc461b9477792d93b83eae" } ],
+ "doc": {
+ "_id": "doc1",
+ "_rev": "1-9152679630cc461b9477792d93b83eae"
+ },
+ "seq": 1
+ },
+ {
+ "id": "doc2",
+ "changes": [ { "rev": "2-9b50a4b63008378e8d0718a9ad05c7af" } ],
+ "doc": {
+ "_id": "doc2",
+ "_rev": "2-9b50a4b63008378e8d0718a9ad05c7af",
+ "_deleted": true
+ },
+ "deleted": true,
+ "seq": 3
+ }
+ ],
+ "last_seq": 3
+}
+{% endhighlight %}
+
+`seq` and `last_seq` correspond to the overall sequence number of the entire database, and it's what is passed in when using `since` (except for the special `'now'`). It is the primary key for the changes feed, and is also used as a checkpointer by the replication algorithm.
+
+#### Single-shot
+
+If you don't specify `{live: true}`, then you can also use `changes()` in the standard
+callback/promise style, and it will be treated as a single-shot request, which
+returns a list of the changes (i.e. what the `'complete'` event emits):
+
+{% include code/start.html id="changes1" type="callback" %}
+{% highlight js %}
+db.changes({
+ limit: 10,
+ since: 0
+}, function (err, response) {
+ if (err) { return console.log(err); }
+ // handle result
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="changes1" type="async" %}
+{% highlight js %}
+try {
+ const result = await db.changes({
+ limit: 10,
+ since: 0
+ });
+} catch (err) {
+ console.log(err);
+}
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="changes1" type="promise" %}
+{% highlight js %}
+db.changes({
+ limit: 10,
+ since: 0
+}).then(function (result) {
+ // handle result
+}).catch(function (err) {
+ console.log(err);
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+#### Example Response:
+
+{% highlight js %}
+{
+ "results": [{
+ "id": "0B3358C1-BA4B-4186-8795-9024203EB7DD",
+ "seq": 1,
+ "changes": [{
+ "rev": "1-5782E71F1E4BF698FA3793D9D5A96393"
+ }]
+ }, {
+ "id": "mydoc",
+ "seq": 2,
+ "changes": [{
+ "rev": "1-A6157A5EA545C99B00FF904EEF05FD9F"
+ }]
+ }, {
+ "id": "otherdoc",
+ "seq": 3,
+ "changes": [{
+ "rev": "1-3753476B70A49EA4D8C9039E7B04254C"
+ }]
+ }, {
+ "id": "828124B9-3973-4AF3-9DFD-A94CE4544005",
+ "seq": 4,
+ "changes": [{
+ "rev": "1-A8BC08745E62E58830CA066D99E5F457"
+ }]
+ }],
+ "last_seq": 4
+}
+{% endhighlight %}
+
+When `live` is `false`, the returned object is also an event emitter as well as a promise,
+and will fire the `'complete'` event when the results are ready.
+
+Note that this `'complete'` event only fires when you aren't doing live changes.
+
+#### Filtered changes
+
+As with [replicate()](#replication), you can filter using:
+
+* an ad-hoc `filter` function
+* an array of `doc_ids`
+* a `filter` function inside of a design document
+* a `filter` function inside of a design document, with `query_params`
+* a `view` function inside of a design document
+
+If you are running `changes()` on a remote CouchDB, then the first method will run client-side, whereas the last four will filter on the server side. Therefore the last four should be preferred, especially if the database is large, because you want to send as few documents over the wire as possible.
+
+If you are running `changes()` on a local PouchDB, then obviously all five methods will run client-side. There are also no performance benefits to using any of the five, so can also just filter yourself, in your own `on('change')` handler. These methods are implemented in PouchDB purely for consistency with CouchDB.
+
+The named functions can either be specified with `'designdoc_id/function_name'` or (if both design doc id and function name are equal) as `'fname'` as shorthand for `'fname/fname'`.
+
+#### Filtering examples
+
+In these examples, we'll work with some mammals. Let's imagine our docs are:
+
+{% highlight js %}
+[
+ {_id: 'a', name: 'Kangaroo', type: 'marsupial'},
+ {_id: 'b', name: 'Koala', type: 'marsupial'},
+ {_id: 'c', name: 'Platypus', type: 'monotreme'}
+]
+{% endhighlight %}
+
+Here are 5 examples using the 5 different systems.
+
+**Example 1: Ad-hoc `filter` function**
+
+*Warning*: this runs client-side, if the database is remote.
+
+Filter by `type === 'marsupial'`:
+
+{% highlight js %}
+db.changes({
+ filter: function (doc) {
+ return doc.type === 'marsupial';
+ }
+});
+{% endhighlight %}
+
+**Example 2: Array of `doc_ids`**
+
+Filter documents with `_id`s `['a', 'c']`.
+
+{% highlight js %}
+db.changes({
+ doc_ids: ['a', 'c']
+});
+{% endhighlight %}
+
+**Example 3: `filter` function inside of a design document**
+
+First `put()` a design document:
+
+{% highlight js %}
+{
+ _id: '_design/mydesign',
+ filters: {
+ myfilter: function (doc) {
+ return doc.type === 'marsupial';
+ }.toString()
+ }
+}
+{% endhighlight %}
+
+Then filter by `type === 'marsupial'`:
+
+{% highlight js %}
+db.changes({
+ filter: 'mydesign/myfilter'
+});
+{% endhighlight %}
+
+**Example 4: `filter` function inside of a design document, with `query_params`**
+
+This is the most powerful way to filter, because it allows you to pass in arbitrary options to your filter function.
+
+First `put()` a design document:
+
+{% highlight js %}
+{
+ _id: '_design/myfilter',
+ filters: {
+ myfilter: function (doc, req) {
+ return doc.type === req.query.type;
+ }.toString()
+ }
+}
+{% endhighlight %}
+
+Then filter by `type === 'marsupial'`:
+
+{% highlight js %}
+db.changes({
+ filter: 'myfilter',
+ query_params: {type: 'marsupial'}
+});
+{% endhighlight %}
+
+Since both the design document and the filter function have the same name, we can shorten the function name to `'myfilter'`.
+
+**Example 5: `view` function inside of a design document**
+
+This doesn't really offer any advantages compared to the previous two methods, unless you are already using a `view` for map/reduce queries, and you want to reuse it.
+
+Any documents that `emit()` anything will be considered to have passed this filter method.
+
+First `put()` a design document:
+
+{% highlight js %}
+{
+ _id: '_design/mydesign',
+ views: {
+ myview: function (doc) {
+ if (doc.type === 'marsupial') {
+ emit(doc._id);
+ }
+ }.toString()
+ }
+}
+{% endhighlight %}
+
+Then filter by `type === 'marsupial'`:
+
+{% highlight js %}
+db.changes({
+ filter: '_view',
+ view: 'mydesign/myview'
+});
+{% endhighlight %}
diff --git a/docs/version/9.0.0/api/close_database.html b/docs/version/9.0.0/api/close_database.html
new file mode 100644
index 0000000000..3e82f1563e
--- /dev/null
+++ b/docs/version/9.0.0/api/close_database.html
@@ -0,0 +1,33 @@
+{% include anchor.html edit="true" title="Close a database" hash="close_database"%}
+
+{% highlight js %}
+db.close([callback])
+{% endhighlight %}
+
+Close the database, this closes any open connection to the underlying storage and frees memory (event listeners) the database may be using.
+
+#### Example Usage
+
+{% include code/start.html id="close_db" type="callback" %}
+{% highlight js %}
+db.close(function () {
+ // success
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="close_db" type="async" %}
+
+{% highlight js %}
+await db.close();
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="close_db" type="promise" %}
+
+{% highlight js %}
+db.close().then(function () {
+ // success
+});
+{% endhighlight %}
+{% include code/end.html %}
diff --git a/docs/version/9.0.0/api/compaction.html b/docs/version/9.0.0/api/compaction.html
new file mode 100644
index 0000000000..c936375d54
--- /dev/null
+++ b/docs/version/9.0.0/api/compaction.html
@@ -0,0 +1,49 @@
+{% include anchor.html edit="true" title="Compact the database" hash="compaction" %}
+
+{% highlight js %}
+db.compact([options], [callback])
+{% endhighlight %}
+
+Triggers a compaction operation in the local or remote database. This reduces the database's size by removing unused and old data, namely non-leaf revisions and attachments that are no longer referenced by those revisions. Note that this is a separate operation from [`viewCleanup()`](#view_cleanup).
+
+For remote databases, PouchDB checks the compaction status at regular intervals and fires the callback (or resolves the promise) upon completion. Consult the [compaction section of CouchDB's maintenance documentation](http://couchdb.readthedocs.org/en/latest/maintenance/compaction.html) for more details.
+
+Also see [auto-compaction](#create_database), which runs compaction automatically (local databases only).
+
+* `options.interval`: Number of milliseconds to wait before asking again if compaction is already done. Defaults to 200. (Only applies to remote databases.)
+
+#### Example Usage:
+
+{% include code/start.html id="compact" type="callback" %}
+{% highlight js %}
+db.compact(function (err, result) {
+ if (err) { return console.log(err); }
+ // handle result
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="compact" type="async" %}
+{% highlight js %}
+try {
+ const result = await db.compact();
+} catch (err) {
+ console.log(err);
+}
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="compact" type="promise" %}
+{% highlight js %}
+db.compact().then(function (result) {
+ // handle result
+}).catch(function (err) {
+ console.log(err);
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+#### Example Response:
+{% highlight js %}
+{ "ok" : "true" }
+{% endhighlight %}
diff --git a/docs/version/9.0.0/api/create_database.html b/docs/version/9.0.0/api/create_database.html
new file mode 100644
index 0000000000..d2792ca0fc
--- /dev/null
+++ b/docs/version/9.0.0/api/create_database.html
@@ -0,0 +1,64 @@
+{% include anchor.html edit="true" title="Create a database" hash="create_database" %}
+
+{% highlight js %}
+new PouchDB([name], [options])
+{% endhighlight %}
+
+This method creates a database or opens an existing one. If you use a URL like `'http://domain.com/dbname'`, then PouchDB will work as a client to an online CouchDB instance. Otherwise it will create a local database using [whatever backend is present](/adapters.html).
+
+### Options
+
+* `name`: You can omit the `name` argument and specify it via `options` instead. Note that the name is required.
+
+**Options for local databases:**
+
+* `auto_compaction`: This turns on auto compaction, which means `compact()` is called after every change to the database. Defaults to `false`.
+* `adapter`: One of `'indexeddb'`, `'idb'`, `'leveldb'`, `'nodesqlite'`, or `'http'`.
+* `revs_limit`: Specify how many old revisions we keep track (not a copy) of. Specifying a low value means Pouch may not be able to figure out whether a new revision received via replication is related to any it currently has which could result in a conflict. Defaults to `1000`.
+* `deterministic_revs`: Use a md5 hash to create a deterministic revision number for documents. Setting it to false will mean that the revision number will be a random UUID. Defaults to true.
+* `view_update_changes_batch_size`: Specify how many change records will be consumed at a time when rebuilding view indexes when the `query()` method is used. Defaults to 50.
+* `view_adapter`: Specify a different adapter to store the view index data.
+* `purged_infos_limit`: Specify how many purged revisions we keep track of. Specifying a low value can result in Pouch not delegating older purges down to views. Defaults to `1000`.
+
+**Options for remote databases:**
+
+* `fetch(url, opts)`: Intercept or override the HTTP request, you can add or modify any headers or options relating to the http request then return a new fetch Promise.
+* `auth.username` + `auth.password`: You can specify HTTP auth parameters either by using a database with a name in the form `http://user:pass@host/name` or via the `auth.username` + `auth.password` options.
+* `skip_setup`: Initially PouchDB checks if the database exists, and tries to create it, if it does not exist yet. Set this to `true` to skip this setup.
+
+**Notes:**
+
+1. In IndexedDB PouchDB will use `_pouch_` to prefix the internal database names. Do not manually create databases with the same prefix.
+2. When acting as a client on Node, any other options given will be passed to [request][].
+3. When using the `'leveldb'` adapter (the default on Node), any other options given will be passed to [levelup][].
+4. If you are using the jwt auth handler, please use the fetch option to add the required headers and handle connected logic like token refreshes.
+
+[request]: https://github.com/mikeal/request
+[levelup]: https://github.com/rvagg/node-levelup
+[levelup_options]: https://github.com/rvagg/node-levelup/#options
+
+#### Example Usage:
+{% highlight js %}
+const db = new PouchDB('dbname');
+// or
+const db = new PouchDB('http://localhost:5984/dbname');
+{% endhighlight %}
+
+Create an in-memory Pouch (must install `pouchdb-adapter-memory` first):
+
+{% highlight js %}
+const db = new PouchDB('dbname', {adapter: 'memory'});
+{% endhighlight %}
+
+Create a remote PouchDB with special fetch options:
+
+{% highlight js %}
+const db = new PouchDB('http://example.com/dbname', {
+ fetch: function (url, opts) {
+ opts.headers.set('X-Some-Special-Header', 'foo');
+ return PouchDB.fetch(url, opts);
+ }
+});
+{% endhighlight %}
+
+For more info, check out [adapters](/adapters.html).
diff --git a/docs/version/9.0.0/api/create_document.html b/docs/version/9.0.0/api/create_document.html
new file mode 100644
index 0000000000..6a377ead31
--- /dev/null
+++ b/docs/version/9.0.0/api/create_document.html
@@ -0,0 +1,174 @@
+{% include anchor.html edit="true" title="Create/update a document" hash="create_document" %}
+
+### Using db.put()
+{% highlight js %}
+db.put(doc, [options], [callback])
+{% endhighlight %}
+
+Create a new document or update an existing document. If the document already exists, you must specify its revision `_rev`, otherwise a conflict will occur.
+
+If you want to update an existing document even if there's conflict, you should specify the base revision `_rev` and use `force=true` option, then a new conflict revision will be created.
+
+`doc` must be a "pure JSON object", i.e. a collection of name/value pairs. If you try to store non-JSON data (for instance `Date` objects) you may see [inconsistent results]({{ site.baseurl }}/errors.html#could_not_be_cloned).
+
+#### Example Usage:
+
+Create a new doc with an `_id` of `'mydoc'`:
+
+{% include code/start.html id="newDoc" type="callback" %}
+{% highlight js %}
+db.put({
+ _id: 'mydoc',
+ title: 'Heroes'
+}, function(err, response) {
+ if (err) { return console.log(err); }
+ // handle response
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="newDoc" type="async" %}
+{% highlight js %}
+try {
+ const response = await db.put({
+ _id: 'mydoc',
+ title: 'Heroes'
+ });
+} catch (err) {
+ console.log(err);
+}
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="newDoc" type="promise" %}
+{% highlight js %}
+db.put({
+ _id: 'mydoc',
+ title: 'Heroes'
+}).then(function (response) {
+ // handle response
+}).catch(function (err) {
+ console.log(err);
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+You can update an existing doc using `_rev`:
+
+{% include code/start.html id="updateDoc" type="callback" %}
+{% highlight js %}
+db.get('mydoc', function(err, doc) {
+ if (err) { return console.log(err); }
+ db.put({
+ _id: 'mydoc',
+ _rev: doc._rev,
+ title: "Let's Dance"
+ }, function(err, response) {
+ if (err) { return console.log(err); }
+ // handle response
+ });
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="updateDoc" type="async" %}
+{% highlight js %}
+try {
+ const doc = await db.get('mydoc');
+ const response = await db.put({
+ _id: 'mydoc',
+ _rev: doc._rev,
+ title: "Let's Dance"
+ });
+} catch (err) {
+ console.log(err);
+}
+{% endhighlight %}
+{% include code/end.html %}
+
+
+{% include code/start.html id="updateDoc" type="promise" %}
+{% highlight js %}
+db.get('mydoc').then(function(doc) {
+ return db.put({
+ _id: 'mydoc',
+ _rev: doc._rev,
+ title: "Let's Dance"
+ });
+}).then(function(response) {
+ // handle response
+}).catch(function (err) {
+ console.log(err);
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+#### Example Response:
+{% highlight js %}
+{
+ "ok": true,
+ "id": "mydoc",
+ "rev": "1-A6157A5EA545C99B00FF904EEF05FD9F"
+}
+{% endhighlight %}
+
+The response contains the `id` of the document, the new `rev`, and an `ok` to reassure
+you that everything is okay.
+
+### Using db.post()
+
+{% highlight js %}
+db.post(doc, [options], [callback])
+{% endhighlight %}
+
+Create a new document and let PouchDB auto-generate an `_id` for it.
+
+#### Example Usage:
+
+{% include code/start.html id="post_doc" type="callback" %}
+{% highlight js %}
+db.post({
+ title: 'Ziggy Stardust'
+}, function (err, response) {
+ if (err) { return console.log(err); }
+ // handle response
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="post_doc" type="async" %}
+{% highlight js %}
+try {
+ const response = await db.post({
+ title: 'Ziggy Stardust'
+ });
+} catch (err) {
+ console.log(err);
+}
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="post_doc" type="promise" %}
+{% highlight js %}
+db.post({
+ title: 'Ziggy Stardust'
+}).then(function (response) {
+ // handle response
+}).catch(function (err) {
+ console.log(err);
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+#### Example Response:
+{% highlight js %}
+{
+ "ok" : true,
+ "id" : "8A2C3761-FFD5-4770-9B8C-38C33CED300A",
+ "rev" : "1-d3a8e0e5aa7c8fff0c376dac2d8a4007"
+}
+{% endhighlight %}
+
+**Put vs. post**: The basic rule of thumb is: `put()` new documents with an `_id`, `post()` new documents without an `_id`.
+
+You should also prefer `put()` to `post()`, because when you `post()`, you are missing an opportunity to use `allDocs()` to sort documents by `_id` (because your `_id`s are random). For more info, read the [PouchDB pro tips](/2014/06/17/12-pro-tips-for-better-code-with-pouchdb.html).
diff --git a/docs/version/9.0.0/api/create_index.html b/docs/version/9.0.0/api/create_index.html
new file mode 100644
index 0000000000..38b26a61f4
--- /dev/null
+++ b/docs/version/9.0.0/api/create_index.html
@@ -0,0 +1,302 @@
+{% include anchor.html edit="true" title="Create index" hash="create_index" %}
+
+{% highlight js %}
+db.createIndex(index [, callback])
+{% endhighlight %}
+
+Create an index if it doesn't exist, or do nothing if it already exists.
+
+{% include alert/start.html variant="info"%}
+{% markdown %}
+
+**pouchdb-find plugin needed:** This API requires the `pouchdb-find` plugin. See
+[Mango queries](/guides/mango-queries.html) for installation instructions.
+
+{% endmarkdown %}
+{% include alert/end.html%}
+
+#### Example Usage:
+
+{% include code/start.html id="create_idx" type="callback" %}
+{% highlight js %}
+db.createIndex({
+ index: {
+ fields: ['foo']
+ }
+}, function (err, result) {
+ if (err) { return console.log(err); }
+ // handle result
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="create_idx" type="async" %}
+{% highlight js %}
+try {
+ const result = await db.createIndex({
+ index: {
+ fields: ['foo']
+ }
+ });
+} catch (err) {
+ console.log(err);
+}
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="create_idx" type="promise" %}
+{% highlight js %}
+db.createIndex({
+ index: {
+ fields: ['foo']
+ }
+}).then(function (result) {
+ // handle result
+}).catch(function (err) {
+ console.log(err);
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+#### Example Response:
+
+If the index was created, you'll see:
+
+{% highlight js %}
+{ "result": "created" }
+{% endhighlight %}
+
+Or if the index already exists:
+
+{% highlight js %}
+{ "result": "exists" }
+{% endhighlight %}
+
+You can also create an index on multiple fields:
+
+{% include code/start.html id="create_idx2" type="callback" %}
+{% highlight js %}
+db.createIndex({
+ index: {
+ fields: ['foo', 'bar', 'baz']
+ }
+}, function (err, result) {
+ if (err) { return console.log(err); }
+ // handle result
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="create_idx2" type="async" %}
+{% highlight js %}
+try {
+ const result = await db.createIndex({
+ index: {
+ fields: ['foo', 'bar', 'baz']
+ }
+ });
+} catch (err) {
+ console.log(err);
+}
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="create_idx2" type="promise" %}
+{% highlight js %}
+db.createIndex({
+ index: {
+ fields: ['foo', 'bar', 'baz']
+ }
+}).then(function (result) {
+ // handle result
+}).catch(function (err) {
+ console.log(err);
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+Or an index on deep fields:
+
+{% include code/start.html id="create_idx3" type="callback" %}
+{% highlight js %}
+db.createIndex({
+ index: {
+ fields: ['person.address.zipcode']
+ }
+}, function (err, result) {
+ if (err) { return console.log(err); }
+ // handle result
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="create_idx3" type="async" %}
+{% highlight js %}
+try {
+ const result = await db.createIndex({
+ index: {
+ fields: ['person.address.zipcode']
+ }
+ });
+} catch (err) {
+ console.log(err);
+}
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="create_idx3" type="promise" %}
+{% highlight js %}
+db.createIndex({
+ index: {
+ fields: ['person.address.zipcode']
+ }
+}).then(function (result) {
+ // handle result
+}).catch(function (err) {
+ console.log(err);
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+You can also specify additional options, if you want more control over how your index is created:
+
+{% include code/start.html id="create_idx4" type="callback" %}
+{% highlight js %}
+db.createIndex({
+ index: {
+ fields: ['foo', 'bar'],
+ name: 'myindex',
+ ddoc: 'mydesigndoc',
+ type: 'json',
+ }
+}, function (err, result) {
+ if (err) { return console.log(err); }
+ // handle result
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="create_idx4" type="async" %}
+{% highlight js %}
+try {
+ const result = await db.createIndex({
+ index: {
+ fields: ['foo', 'bar'],
+ name: 'myindex',
+ ddoc: 'mydesigndoc',
+ type: 'json',
+ }
+ });
+} catch (err) {
+ console.log(err);
+}
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="create_idx4" type="promise" %}
+{% highlight js %}
+db.createIndex({
+ index: {
+ fields: ['foo', 'bar'],
+ name: 'myindex',
+ ddoc: 'mydesigndoc',
+ type: 'json',
+ }
+}).then(function (result) {
+ // handle result
+}).catch(function (err) {
+ console.log(err);
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+If you want to index a subset of the documents in the database, you can use `partial_filter_selector`. This has the same syntax as the selector you'd pass to `find()`, and only documents matching the selector will be included in the index.
+
+{% include alert/start.html variant="warning"%}
+{% markdown %}
+
+**When using _pouchdb-adapter-indexeddb:_** On the `indexeddb`-adapter, indexes with
+a`partial_filter_selector` will fall back to using [map-reduce](#query_database),
+instead of the native indexes, negating all index performance gains from the
+`indexeddb`-adapter over the `idb`-adapter.
+
+{% endmarkdown %}
+{% include alert/end.html%}
+
+{% include code/start.html id="create_idx5" type="callback" %}
+{% highlight js %}
+db.createIndex({
+ index: {
+ fields: ['year', 'title'],
+ partial_filter_selector: {
+ year: { $gt: 2010 }
+ }
+ }
+}, function (err, result) {
+ if (err) { return console.log(err); }
+ // handle result
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="create_idx5" type="async" %}
+{% highlight js %}
+try {
+ const result = db.createIndex({
+ index: {
+ fields: ['year', 'title'],
+ partial_filter_selector: {
+ year: { $gt: 2010 }
+ }
+ }
+ });
+} catch (err) {
+ console.log(err);
+}
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="create_idx5" type="promise" %}
+{% highlight js %}
+db.createIndex({
+ index: {
+ fields: ['year', 'title'],
+ partial_filter_selector: {
+ year: { $gt: 2010 }
+ }
+ }
+}).then(function (result) {
+ // handle result
+}).catch(function (err) {
+ console.log(err);
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+### Options
+
+* `fields`: a list of fields to index
+* `name` (optional): name of the index, auto-generated if you don't include it
+* `ddoc` (optional): design document name (i.e. the part after `'_design/'`), auto-generated if you don't include it
+* `type` (optional): only supports `'json'`, which is also the default
+* `partial_filter_selector` (optional): a _selector_ used to filter the set of documents included in the index
+
+When a PouchDB instance updates an index, it emits `indexing` events that include information about the progress of the index update task.
+
+{% highlight js %}
+const db = new PouchDB('my-docs');
+
+db.on('indexing', function (event) {
+ // called when indexes are updated
+});
+{% endhighlight %}
+
+The `event` object contains the following fields:
+
+* `view`: the name of the view that's being updated
+* `indexed_docs`: the total number of document updates processed during the current run
+
+When an index is updated this event is emitted once at the start of the update, with `indexed_docs` equal to `0`. It will then emit further events as batches of changes are processed, and those events will also include these fields:
+
+* `last_seq`: the `seq` value of the last event from the database change feed that's been processed
+* `results_count`: the number of changes in the most recent batch of changes
diff --git a/docs/version/9.0.0/api/database_information.html b/docs/version/9.0.0/api/database_information.html
new file mode 100644
index 0000000000..e2cb79ff5f
--- /dev/null
+++ b/docs/version/9.0.0/api/database_information.html
@@ -0,0 +1,59 @@
+{% include anchor.html edit="true" title="Get database information" hash="database_information" %}
+
+{% highlight js %}
+db.info([callback])
+{% endhighlight %}
+
+Get information about a database.
+
+#### Example Usage:
+
+{% include code/start.html id="dbinfo" type="callback" %}
+{% highlight js %}
+db.info(function(err, info) {
+ if (err) { return console.log(err); }
+ // handle result
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="dbinfo" type="async" %}
+{% highlight js %}
+try {
+ const result = await db.info();
+} catch (err) {
+ console.log(err);
+}
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="dbinfo" type="promise" %}
+{% highlight js %}
+db.info().then(function (result) {
+ // handle result
+}).catch(function (err) {
+ console.log(err);
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+#### Example Response:
+{% highlight js %}
+{
+ "db_name": "test",
+ "doc_count": 4,
+ "update_seq": 5
+}
+{% endhighlight %}
+
+**Response object:**
+
+* `db_name` is the name of the database you gave when you called `new PouchDB()`, and also the unique identifier for the database.
+* `doc_count` is the total number of non-deleted documents in the database.
+* `update_seq` is the sequence number of the database. It starts at 0 and gets incremented every time a document is added or modified.
+
+There are also some details you can use for debugging. These are unofficial and may change at any time:
+
+* `adapter`: The name of the adapter being used (idb, leveldb, ...).
+* `idb_attachment_format`: (IndexedDB) either `'base64'` or `'binary'`, depending on whether the browser [supports binary blobs](/faq.html#data_types).
+* `backend_adapter`: (Node.JS) the backend *DOWN adapter being used (MemDOWN, RiakDOWN, ...).
diff --git a/docs/version/9.0.0/api/defaults.html b/docs/version/9.0.0/api/defaults.html
new file mode 100644
index 0000000000..de2b22cab5
--- /dev/null
+++ b/docs/version/9.0.0/api/defaults.html
@@ -0,0 +1,42 @@
+{% include anchor.html edit="true" title="Default settings" hash="defaults" %}
+
+If you find yourself using the same constructor options repeatedly,
+you can simplify your code with `PouchDB.defaults()`:
+
+{% highlight js %}
+PouchDB.defaults({
+ option1: 'foo',
+ option2: 'value'
+});
+{% endhighlight %}
+
+The returned object is a constructor function that works the same as `PouchDB`, except that whenever you invoke it (e.g. with `new`), the given options will be passed in by default.
+
+#### Example Usage:
+{% highlight js %}
+const MyMemPouch = PouchDB.defaults({
+ adapter: 'memory'
+});
+// In-memory PouchDB
+const myMemPouch = new MyMemPouch('dbname');
+
+const MyPrefixedPouch = PouchDB.defaults({
+ prefix: '/path/to/my/db/'
+});
+// db will be named '/path/to/my/db/dbname', useful for LevelDB
+const myPrefixedPouch = new MyPrefixedPouch('dbname');
+
+const HTTPPouch = PouchDB.defaults({
+ prefix: 'http://example.org'
+});
+
+// db will be located at 'http://example.org/dbname'
+const myHttpPouch = new HTTPPouch('dbname');
+{% endhighlight %}
+
+Note the special constructor option `prefix`, which appends a prefix to the database name
+and can be helpful for URL-based or file-based LevelDOWN path names.
+
+All [constructor options](#create_database) are supported. Default options can still be overriden individually.
+
+
diff --git a/docs/version/9.0.0/api/delete_attachment.html b/docs/version/9.0.0/api/delete_attachment.html
new file mode 100644
index 0000000000..bceff6a70c
--- /dev/null
+++ b/docs/version/9.0.0/api/delete_attachment.html
@@ -0,0 +1,52 @@
+{% include anchor.html edit="true" title="Delete an attachment" hash="delete_attachment" %}
+
+{% highlight js %}
+db.removeAttachment(docId, attachmentId, rev, [callback])
+{% endhighlight %}
+
+Delete an attachment from a doc. You must supply the `rev` of the existing doc.
+
+#### Example Usage:
+
+{% include code/start.html id="delete_att" type="callback" %}
+{% highlight js %}
+const rev = '1-068E73F5B44FEC987B51354DFC772891';
+db.removeAttachment('doc', 'att.txt', rev, function(err, res) {
+ if (err) { return console.log(err); }
+ // handle result
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="delete_att" type="async" %}
+{% highlight js %}
+try {
+ const rev = '1-068E73F5B44FEC987B51354DFC772891';
+ const result = await db.removeAttachment('doc', 'att.txt', rev);
+} catch (err) {
+ console.log(err);
+}
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="delete_att" type="promise" %}
+{% highlight js %}
+const rev = '1-068E73F5B44FEC987B51354DFC772891';
+db.removeAttachment('doc', 'att.txt', rev).then(function (result) {
+ // handle result
+}).catch(function (err) {
+ console.log(err);
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+#### Example Response:
+
+{% highlight js %}
+{
+ "ok": true,
+ "rev": "2-1F983211AB87EFCCC980974DFC27382F"
+}
+{% endhighlight %}
+
+
diff --git a/docs/version/9.0.0/api/delete_database.html b/docs/version/9.0.0/api/delete_database.html
new file mode 100644
index 0000000000..69cca3cad8
--- /dev/null
+++ b/docs/version/9.0.0/api/delete_database.html
@@ -0,0 +1,50 @@
+{% include anchor.html edit="true" title="Delete a database" hash="delete_database"%}
+
+{% highlight js %}
+db.destroy([options], [callback])
+{% endhighlight %}
+
+Delete the database. Note that this has no impact on other replicated databases.
+
+#### Example Usage
+
+{% include code/start.html id="destroy_db" type="callback" %}
+{% highlight js %}
+db.destroy(function (err, response) {
+ if (err) {
+ return console.log(err);
+ } else {
+ // success
+ }
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="destroy_db" type="async" %}
+
+{% highlight js %}
+try {
+ await db.destroy();
+} catch (err) {
+ console.log(err);
+}
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="destroy_db" type="promise" %}
+
+{% highlight js %}
+db.destroy().then(function (response) {
+ // success
+}).catch(function (err) {
+ console.log(err);
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+#### Example Response:
+{% highlight js %}
+{
+ "ok" : true
+}
+{% endhighlight %}
diff --git a/docs/version/9.0.0/api/delete_document.html b/docs/version/9.0.0/api/delete_document.html
new file mode 100644
index 0000000000..c6e11c7c15
--- /dev/null
+++ b/docs/version/9.0.0/api/delete_document.html
@@ -0,0 +1,140 @@
+{% include anchor.html edit="true" title="Delete a document" hash="delete_document"%}
+
+{% highlight js %}
+db.remove(doc, [options], [callback])
+{% endhighlight %}
+
+Or:
+
+{% highlight js %}
+db.remove(docId, docRev, [options], [callback])
+{% endhighlight %}
+
+
+Deletes the document. `doc` is required to be a document with at least an `_id` and a `_rev` property. Sending the full document will work as well.
+
+See [filtered replication]({{ site.baseurl }}/api.html#filtered-replication) for why you might want to use `put()` with `{_deleted: true}` instead.
+
+#### Example Usage:
+
+{% include code/start.html id="delete_doc" type="callback" %}
+{% highlight js %}
+db.get('mydoc', function(err, doc) {
+ if (err) { return console.log(err); }
+ db.remove(doc, function(err, response) {
+ if (err) { return console.log(err); }
+ // handle response
+ });
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="delete_doc" type="async" %}
+{% highlight js %}
+try {
+ const doc = await db.get('mydoc');
+ const response = await db.remove(doc);
+} catch (err) {
+ console.log(err);
+}
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="delete_doc" type="promise" %}
+{% highlight js %}
+db.get('mydoc').then(function(doc) {
+ return db.remove(doc);
+}).then(function (result) {
+ // handle result
+}).catch(function (err) {
+ console.log(err);
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+#### Example Response:
+
+{% highlight js %}
+{
+ "ok": true,
+ "id": "mydoc",
+ "rev": "2-9AF304BE281790604D1D8A4B0F4C9ADB"
+}
+{% endhighlight %}
+
+You can also delete a document by just providing an `id` and `rev`:
+
+{% include code/start.html id="delete_doc3" type="callback" %}
+{% highlight js %}
+db.get('mydoc', function(err, doc) {
+ if (err) { return console.log(err); }
+ db.remove(doc._id, doc._rev, function(err, response) {
+ if (err) { return console.log(err); }
+ // handle response
+ });
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="delete_doc3" type="async" %}
+{% highlight js %}
+try {
+ const doc = await db.get('mydoc');
+ const response = await db.remove(doc._id, doc._rev);
+} catch (err) {
+ console.log(err);
+}
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="delete_doc3" type="promise" %}
+{% highlight js %}
+db.get('mydoc').then(function(doc) {
+ return db.remove(doc._id, doc._rev);
+}).then(function (result) {
+ // handle result
+}).catch(function (err) {
+ console.log(err);
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+You can also delete a document by using `put()` with `{_deleted: true}`:
+
+{% include code/start.html id="delete_doc2" type="callback" %}
+{% highlight js %}
+db.get('mydoc', function(err, doc) {
+ if (err) { return console.log(err); }
+ doc._deleted = true;
+ db.put(doc, function(err, response) {
+ if (err) { return console.log(err); }
+ // handle response
+ });
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="delete_doc2" type="async" %}
+{% highlight js %}
+try {
+ const doc = await db.get('mydoc');
+ doc._deleted = true;
+ const result = await db.put(doc);
+} catch (err) {
+ console.log(err);
+}
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="delete_doc2" type="promise" %}
+{% highlight js %}
+db.get('mydoc').then(function(doc) {
+ doc._deleted = true;
+ return db.put(doc);
+}).then(function (result) {
+ // handle result
+}).catch(function (err) {
+ console.log(err);
+});
+{% endhighlight %}
+{% include code/end.html %}
diff --git a/docs/version/9.0.0/api/delete_index.html b/docs/version/9.0.0/api/delete_index.html
new file mode 100644
index 0000000000..733bd743c1
--- /dev/null
+++ b/docs/version/9.0.0/api/delete_index.html
@@ -0,0 +1,129 @@
+{% include anchor.html edit="true" title="Delete index" hash="delete_index" %}
+
+{% highlight js %}
+db.deleteIndex(index [, callback])
+{% endhighlight %}
+
+Delete an index, remove any orphaned design documents, and clean up any leftover data on disk.
+
+{% include alert/start.html variant="info"%}
+{% markdown %}
+
+**pouchdb-find plugin needed:** This API requires the `pouchdb-find` plugin. See
+[Mango queries](/guides/mango-queries.html) for installation instructions.
+
+{% endmarkdown %}
+{% include alert/end.html%}
+
+
+#### Example Usage:
+
+{% include code/start.html id="delete_idx" type="callback" %}
+{% highlight js %}
+db.deleteIndex({
+ "ddoc": "_design/idx-0f3a6f73110868266fa5c688caf8acd3",
+ "name": "idx-0f3a6f73110868266fa5c688caf8acd3",
+ "type": "json",
+ "def": {
+ "fields": [
+ { "foo": "asc" },
+ { "bar": "asc" }
+ ]
+ }
+}, function (err, result) {
+ if (err) { return console.log(err); }
+ // handle result
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="delete_idx" type="async" %}
+{% highlight js %}
+try {
+ const result = await db.deleteIndex({
+ "ddoc": "_design/idx-0f3a6f73110868266fa5c688caf8acd3",
+ "name": "idx-0f3a6f73110868266fa5c688caf8acd3",
+ "type": "json",
+ "def": {
+ "fields": [
+ { "foo": "asc" },
+ { "bar": "asc" }
+ ]
+ }
+ });
+} catch (err) {
+ console.log(err);
+}
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="delete_idx" type="promise" %}
+{% highlight js %}
+db.deleteIndex({
+ "ddoc": "_design/idx-0f3a6f73110868266fa5c688caf8acd3",
+ "name": "idx-0f3a6f73110868266fa5c688caf8acd3",
+ "type": "json",
+ "def": {
+ "fields": [
+ { "foo": "asc" },
+ { "bar": "asc" }
+ ]
+ }
+}).then(function (result) {
+ // handle result
+}).catch(function (err) {
+ console.log(err);
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+#### Example Response:
+
+{% highlight js %}
+{ "ok": true }
+{% endhighlight %}
+
+Note that the easiest way to do this is to locate the index you want to delete using [`getIndexes()`](#list_indexes).
+For instance, here is how you would delete the second index from that list (which should be the
+one after the built-in `_all_docs` index):
+
+{% include code/start.html id="delete_idx2" type="callback" %}
+{% highlight js %}
+db.getIndexes(function (err, indexesResult) {
+ if (err) { return console.log(err); }
+ db.deleteIndex(indexesResult.indexes[1], function (err, result) {
+ if (err) { return console.log(err); }
+ // handle result
+ });
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="delete_idx2" type="async" %}
+{% highlight js %}
+try {
+ const indexesResult = await db.getIndexes();
+ const result = await db.deleteIndex(indexesResult.indexes[1]);
+} catch (err) {
+ console.log(err);
+}
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="delete_idx2" type="promise" %}
+{% highlight js %}
+db.getIndexes().then(function (indexesResult) {
+ return db.deleteIndex(indexesResult.indexes[1]);
+}).then(function (result) {
+ // handle result
+}).catch(function (err) {
+ console.log(err);
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+**Notes:**
+
+* You don't need to provide a `_rev` when deleting an index.
+* The associated design doc is automatically deleted, assuming it only contains one index.
+* There is no need to call [`viewCleanup`](#view_cleanup) to clean up any leftover data. `deleteIndex()` does this automatically for you.
diff --git a/docs/version/9.0.0/api/events.html b/docs/version/9.0.0/api/events.html
new file mode 100644
index 0000000000..1e7563e305
--- /dev/null
+++ b/docs/version/9.0.0/api/events.html
@@ -0,0 +1,14 @@
+{% include anchor.html edit="true" title="Events" hash="events" %}
+
+PouchDB is an [event emitter][event emitter] and will emit a `'created'` event when a database is created. A `'destroyed'` event is emitted when a database is destroyed.
+
+{% highlight js %}
+PouchDB.on('created', function (dbName) {
+ // called whenever a db is created.
+});
+PouchDB.on('destroyed', function (dbName) {
+ // called whenever a db is destroyed.
+});
+{% endhighlight %}
+
+
diff --git a/docs/version/9.0.0/api/explain_index.html b/docs/version/9.0.0/api/explain_index.html
new file mode 100644
index 0000000000..e79cd50e2c
--- /dev/null
+++ b/docs/version/9.0.0/api/explain_index.html
@@ -0,0 +1,135 @@
+{% include anchor.html edit="true" title="Explain index" hash="explain_index" %}
+
+{% highlight js %}
+db.explain(request [, callback])
+{% endhighlight %}
+
+Explain the query plan for a given query
+
+{% include alert/start.html variant="info"%}
+{% markdown %}
+
+**pouchdb-find plugin needed:** This API requires the `pouchdb-find` plugin. See
+[Mango queries](/guides/mango-queries.html) for installation instructions.
+
+{% endmarkdown %}
+{% include alert/end.html%}
+
+#### Example Usage:
+
+{% include code/start.html id="explain_idx" type="callback" %}
+{% highlight js %}
+db.explain({
+ selector: {
+ name: 'Mario',
+ series: "mario"
+ },
+ fields: ['_id', 'name'],
+ sort: ['name']
+}, function (err, explanation) {
+ if (err) { return console.log(err); }
+ // view explanation
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="explain_idx" type="async" %}
+{% highlight js %}
+try {
+ const explanation = await db.explain({
+ selector: {
+ name: 'Mario',
+ series: "mario"
+ },
+ fields: ['_id', 'name'],
+ sort: ['name']
+ });
+} catch (err) {
+ console.log(err);
+}
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="explain_idx" type="promise" %}
+{% highlight js %}
+db.explain({
+ selector: {
+ name: 'Mario',
+ series: "mario"
+ },
+ fields: ['_id', 'name'],
+ sort: ['name']
+}).then(function (explanation) {
+ // view explanation
+}).catch(function (err) {
+ console.log(err);
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+#### Example Response:
+
+{% highlight js %}
+{
+ "dbname": "database-name"
+ "index": {
+ "ddoc": "_design/design-doc-name",
+ "name": "index-name",
+ "type": "json",
+ "def": {
+ "fields": [
+ {
+ "name": "asc"
+ },
+ {
+ "series": "asc"
+ }
+ ]
+ }
+ },
+ "selector": {
+ "$and": [
+ {
+ "name": {
+ "$eq": "mario"
+ }
+ },
+ {
+ "series": {
+ "$eq": "mario"
+ }
+ }
+ ]
+ },
+ "opts": {
+ "use_index": [],
+ "bookmark": "nil",
+ "limit": 25,
+ "skip": 0,
+ "sort": {"name": "asc"},
+ "fields": [
+ "_id"
+ ],
+ "r": [
+ 49
+ ],
+ "conflicts": false
+ },
+ "limit": 10,
+ "skip": 1,
+ "fields": [
+ "_id"
+ ],
+ "range": {
+ "start_key": [
+ "mario",
+ "mario"
+ ],
+ "end_key": [
+ "mario",
+ "mario",
+ {}
+ ]
+ }
+};
+{% endhighlight %}
diff --git a/docs/version/9.0.0/api/fetch_document.html b/docs/version/9.0.0/api/fetch_document.html
new file mode 100644
index 0000000000..0897a24f09
--- /dev/null
+++ b/docs/version/9.0.0/api/fetch_document.html
@@ -0,0 +1,64 @@
+
+{% include anchor.html edit="true" title="Fetch a document" hash="fetch_document"%}
+
+{% highlight js %}
+db.get(docId, [options], [callback])
+{% endhighlight %}
+
+Retrieves a document, specified by `docId`.
+
+### Options
+
+All options default to `false` unless otherwise specified.
+
+* `options.rev`: Fetch specific revision of a document. Defaults to winning revision (see [the CouchDB guide](http://guide.couchdb.org/draft/conflicts.html)).
+* `options.revs`: Include revision history of the document.
+* `options.revs_info`: Include a list of revisions of the document, and their availability.
+* `options.open_revs`: Fetch all leaf revisions if `open_revs="all"` or fetch all leaf revisions specified in `open_revs` array. Leaves will be returned in the same order as specified in input array.
+* `options.conflicts`: If specified, conflicting leaf revisions will be attached in `_conflicts` array.
+* `options.attachments`: Include attachment data.
+ * `options.binary`: Return attachment data as Blobs/Buffers, instead of as base64-encoded strings.
+* `options.latest`: Forces retrieving latest “leaf” revision, no matter what rev was requested. Default is `false`.
+
+#### Example Usage:
+
+{% include code/start.html id="get1" type="callback" %}
+{% highlight js %}
+db.get('mydoc', function(err, doc) {
+ if (err) { return console.log(err); }
+ // handle doc
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="get1" type="async" %}
+{% highlight js %}
+try {
+ const doc = await db.get('mydoc');
+} catch (err) {
+ console.log(err);
+}
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="get1" type="promise" %}
+{% highlight js %}
+db.get('mydoc').then(function (doc) {
+ // handle doc
+}).catch(function (err) {
+ console.log(err);
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+#### Example Response:
+{% highlight js %}
+{
+ "_id": "mydoc",
+ "_rev": "1-A6157A5EA545C99B00FF904EEF05FD9F"
+ "title": "Rock and Roll Heart",
+}
+{% endhighlight %}
+
+The response contains the document as it is stored in the database, along with its
+`_id` and `_rev`.
diff --git a/docs/version/9.0.0/api/get_attachment.html b/docs/version/9.0.0/api/get_attachment.html
new file mode 100644
index 0000000000..5578176228
--- /dev/null
+++ b/docs/version/9.0.0/api/get_attachment.html
@@ -0,0 +1,116 @@
+{% include anchor.html edit="true" title="Get an attachment" hash="get_attachment" %}
+
+{% highlight js %}
+db.getAttachment(docId, attachmentId, [options], [callback])
+{% endhighlight %}
+
+Get attachment data.
+
+### Options
+
+* `options.rev`: as with [get()](#fetch_document), you can pass a `rev` in and get back an attachment for the document at that particular revision.
+
+#### Example Usage:
+
+Get an attachment with filename `'att.txt'` from document with ID `'doc'`:
+
+{% include code/start.html id="get_att1" type="callback" %}
+{% highlight js %}
+db.getAttachment('doc', 'att.txt', function(err, blobOrBuffer) {
+ if (err) { return console.log(err); }
+ // handle result
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="get_att1" type="async" %}
+{% highlight js %}
+try {
+ const blobOrBuffer = await db.getAttachment('doc', 'att.txt');
+} catch (err) {
+ console.log(err);
+}
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="get_att1" type="promise" %}
+{% highlight js %}
+db.getAttachment('doc', 'att.txt').then(function (blobOrBuffer) {
+ // handle result
+}).catch(function (err) {
+ console.log(err);
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+Get an attachment with filename `'att.txt'` from document with ID `'doc'`, at
+the revision `'1-abcd'`:
+
+{% include code/start.html id="get_att2" type="callback" %}
+{% highlight js %}
+db.getAttachment('doc', 'att.txt', {rev: '1-abcd'}, function(err, blobOrBuffer) {
+ if (err) { return console.log(err); }
+ // handle result
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="get_att2" type="async" %}
+{% highlight js %}
+try {
+ const blobOrBuffer = await db.getAttachment('doc', 'att.txt', {rev: '1-abcd'});
+} catch (err) {
+ console.log(err);
+}
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="get_att2" type="promise" %}
+{% highlight js %}
+db.getAttachment('doc', 'att.txt', {rev: '1-abcd'}).then(function (blobOrBuffer) {
+ // handle result
+}).catch(function (err) {
+ console.log(err);
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+#### Response type:
+
+The response will be a `Blob` object in the browser, and a `Buffer` object in Node.js. See [blob-util](https://github.com/nolanlawson/blob-util) for utilities to transform `Blob`s to other formats, such as base64-encoded strings, data URLs, array buffers, etc.
+
+#### Inline base64 attachments
+
+You can specify `{attachments: true}` to most "read" operations, such as `get()`, `allDocs()`, `changes()`, and `query()`. The attachment data will then be included inlined in the resulting doc(s). However, it will always be supplied as base64. For example:
+
+{% highlight js %}
+{
+ "_attachments": {
+ "att.txt": {
+ "content_type": "text/plain",
+ "digest": "d5ccfd24a8748bed4e2c9a279a2b6089",
+ "data": "SXMgdGhlcmUgbGlmZSBvbiBNYXJzPw=="
+ }
+ },
+ "_id": "mydoc",
+ "_rev": "1-e147d9ec9c85139dfe7e93bc17148d1a"
+}
+{% endhighlight %}
+
+For such APIs, when you don't specify `{attachments: true}`, you will instead get metadata about the attachments. For example:
+
+{% highlight js %}
+{
+ "_attachments": {
+ "att.txt": {
+ "content_type": "text/plain",
+ "digest": "d5ccfd24a8748bed4e2c9a279a2b6089",
+ "stub": true
+ }
+ },
+ "_id": "mydoc",
+ "_rev": "1-e147d9ec9c85139dfe7e93bc17148d1a"
+}
+{% endhighlight %}
+
+This "summary" operation may be faster in some cases, because the attachment itself does not need to be read from disk.
\ No newline at end of file
diff --git a/docs/version/9.0.0/api/list_indexes.html b/docs/version/9.0.0/api/list_indexes.html
new file mode 100644
index 0000000000..fa24c9be11
--- /dev/null
+++ b/docs/version/9.0.0/api/list_indexes.html
@@ -0,0 +1,84 @@
+{% include anchor.html edit="true" title="List indexes" hash="list_indexes" %}
+
+{% highlight js %}
+db.getIndexes([callback])
+{% endhighlight %}
+
+Get a list of all the indexes you've created. Also tells you about the
+special `_all_docs` index, i.e. the default index on the `_id` field.
+
+{% include alert/start.html variant="info"%}
+{% markdown %}
+
+**pouchdb-find plugin needed:** This API requires the `pouchdb-find` plugin. See
+[Mango queries](/guides/mango-queries.html) for installation instructions.
+
+{% endmarkdown %}
+{% include alert/end.html%}
+
+#### Example Usage:
+
+{% include code/start.html id="get_idxs" type="callback" %}
+{% highlight js %}
+db.getIndexes(function (err, result) {
+ if (err) { return console.log(err); }
+ // handle result
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="get_idxs" type="async" %}
+{% highlight js %}
+try {
+ const result = await db.getIndexes();
+} catch (err) {
+ console.log(err);
+}
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="get_idxs" type="promise" %}
+{% highlight js %}
+db.getIndexes().then(function (result) {
+ // handle result
+}).catch(function (err) {
+ console.log(err);
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+#### Example Response:
+
+{% highlight js %}
+{
+ "indexes": [
+ {
+ "ddoc": null,
+ "name": "_all_docs",
+ "type": "special",
+ "def": {
+ "fields": [
+ {
+ "_id": "asc"
+ }
+ ]
+ }
+ },
+ {
+ "ddoc": "_design/idx-0f3a6f73110868266fa5c688caf8acd3",
+ "name": "idx-0f3a6f73110868266fa5c688caf8acd3",
+ "type": "json",
+ "def": {
+ "fields": [
+ {
+ "foo": "asc"
+ },
+ {
+ "bar": "asc"
+ }
+ ]
+ }
+ }
+ ]
+}
+{% endhighlight %}
\ No newline at end of file
diff --git a/docs/version/9.0.0/api/overview.html b/docs/version/9.0.0/api/overview.html
new file mode 100644
index 0000000000..02df9305fd
--- /dev/null
+++ b/docs/version/9.0.0/api/overview.html
@@ -0,0 +1,59 @@
+{% include anchor.html edit="true" title="API overview" hash="overview" %}
+
+PouchDB has an asynchronous API, supporting [callbacks](http://docs.nodejitsu.com/articles/getting-started/control-flow/what-are-callbacks), [promises][promise], and
+[async functions](https://jakearchibald.com/2014/es7-async-functions/). For beginners, we recommend promises, although you are free to use whatever format you prefer. If you are unsure, check out our [guide to asynchronous code](/guides/async-code.html).
+
+Most of the API is exposed as:
+
+{% highlight js %}
+db.doSomething(args..., [options], [callback])
+{% endhighlight %}
+
+… where both the `options` and `callback` are optional.
+
+### Callbacks
+
+Callbacks use the standard Node.js idiom of:
+
+{% highlight js %}
+function(error, result) { /* ... */ }
+{% endhighlight %}
+
+… where the `error` will be undefined if there's no error.
+
+### Promises
+
+If you don't specify a `callback`, then the API returns a [promise][]. In [supported browsers](http://caniuse.com/#feat=promises) or Node.js, native promises are used, falling back to the minimal library [lie][] as needed.
+
+{% include alert/start.html variant="info"%}
+{% markdown %}
+
+**Using Ionic/AngularJS?** You can wrap PouchDB promises in [`$q.when()`](https://docs.angularjs.org/api/ng/service/$q#when). This will notify AngularJS to update the UI when the PouchDB promise has resolved.
+
+{% endmarkdown %}
+{% include alert/end.html%}
+
+To use a custom promise implementation with PouchDB, you must redefine a global `Promise` object before loading PouchDB:
+
+{% highlight html %}
+
+
+{% endhighlight %}
+
+### Async functions
+
+Another option is to use `async`/`await` pattern instead of promises chain (ie. `.then().catch()`). `async`/`await` is widely supported by all major browsers and Node.js. Use a transpiler with `async`/`await` polyfill such as [Babel](http://babeljs.io/) if you need to support older browsers.
+
+Note that the samples for `async`/`await` in the API documentation assume that your code is inside an async function. So for instance:
+
+{% highlight js %}
+async function myFunction() {
+ // your code goes in here
+}
+{% endhighlight %}
+
+Any `await` not inside of an async function is a syntax error. For more information about `async`/`await`, read [our introductory blog post]({{ site.baseurl }}/2015/03/05/taming-the-async-beast-with-es7.html).
+
+[promise]: https://www.promisejs.org/
+[lie]: https://github.com/calvinmetcalf/lie
+[event emitter]: http://nodejs.org/api/events.html#events_class_events_eventemitter
diff --git a/docs/version/9.0.0/api/plugins.html b/docs/version/9.0.0/api/plugins.html
new file mode 100644
index 0000000000..06b29c1113
--- /dev/null
+++ b/docs/version/9.0.0/api/plugins.html
@@ -0,0 +1,121 @@
+{% include anchor.html edit="true" title="Plugins" hash="plugins" %}
+
+{% include alert/start.html variant="info"%}
+{% markdown %}
+
+**This section covers _authoring_ plugins.** For a list of third-party plugins,
+see [Plugins](/external.html), or for a list of first-party plugins that you can
+use to customize the PouchDB build, see [Custom Builds](/custom.html).
+
+{% endmarkdown %}
+{% include alert/end.html%}
+
+Writing a plugin is easy! The API is:
+
+{% highlight js %}
+PouchDB.plugin({
+ methodName: myFunction
+});
+{% endhighlight %}
+
+This will add a `db.methodName()` to all databases, which runs `myFunction`.It will always be called in context, so that within the function, `this` refers to the database object.
+
+There is a [PouchDB Plugin Seed project](https://github.com/pouchdb/plugin-seed), which is the fastest way to get started writing, building and testing your very own plugin.
+
+#### Example Usage:
+{% highlight js %}
+PouchDB.plugin({
+ sayHello : function () {
+ console.log("Hello!");
+ }
+});
+new PouchDB('foobar').sayHello(); // prints "Hello!"
+{% endhighlight %}
+
+Alternatively, instead of passing in an object to `.plugin()`, you can pass in
+a function that takes the `PouchDB` object and performs whatever operations
+you want on it. You can use this to load multiple plugins, add adapters,
+or attach event listeners to the `PouchDB` object.
+
+#### Example Usage:
+{% highlight js %}
+PouchDB.plugin(function (PouchDB) {
+ PouchDB.hello = 'world';
+});
+console.log(PouchDB.hello); // prints "world"
+{% endhighlight %}
+
+(Most likely, if you are writing a PouchDB plugin, you will export either the
+object-style or the function-style plugin, so that your users can then
+attach it to their PouchDB object.)
+
+#### Load Plugins from require()
+
+You can load plugins into PouchDB when you load it via `require()`.
+
+{% highlight js %}
+const greet = {sayHello: function() { console.log("Hello!"); }};
+
+const PouchDB = require('pouchdb').plugin(greet);
+
+const db = new PouchDB('foobar');
+db.sayHello(); // prints "Hello!"
+{% endhighlight %}
+
+You can chain plugins, as well:
+
+{% highlight js %}
+const greet = {sayHello: function() { console.log("Hello!"); }};
+const manners = {thank: function(name) { console.log("Thank you, " + name); }};
+
+const PouchDB = require('pouchdb')
+ .plugin(greet)
+ .plugin(manners);
+
+const db = new PouchDB('foobar');
+db.sayHello(); // prints "Hello!"
+db.thank('Mom'); // prints "Thank you, Mom"
+{% endhighlight %}
+
+#### Example Plugin: Intercept Updates
+
+A useful feature of plugins is to intercept updates before they are stored in PouchDB. In this way, a plugin might validate that the data is correct for the application, or even alter documents before they are committed to the database.
+
+The best way to intercept all updates to a PouchDB database is to **override the `bulkDocs()` method**. All changes to PouchDB documents ultimately pass through the `bulkDocs()` method. For example, a call to `put()` will become a `bulkDocs()` call with a "batch" of one document.
+
+Because PouchDB guarantees to plugin authors that all data changes ultimately happen via `bulkDocs()`, it is the ideal place for an application or plugin to intercept updates.
+
+{% highlight js %}
+// Keep a reference to the "upstream" function.
+const pouchBulkDocs = PouchDB.prototype.bulkDocs;
+PouchDB.plugin({bulkDocs: validBulkDocs});
+
+function validBulkDocs(body, options, callback) {
+ if (typeof options == 'function') {
+ callback = options
+ options = {}
+ }
+
+ let docs;
+ if (Array.isArray(body)) {
+ docs = body;
+ } else {
+ docs = body.docs;
+ }
+
+ // All documents must have a .name field.
+ for (let i = 0; i < docs.length; i++) {
+ if (!docs[i].name) {
+ const id = doc._id || '(no _id given)';
+ return callback(new Error('Document is missing .name field: ' + id));
+ }
+ }
+
+ // All documents check out. Pass them to PouchDB.
+ return pouchBulkDocs.call(this, docs, options, callback);
+}
+{% endhighlight %}
+
+The above plugin would return an error if anything ever attempts to store an unnamed document, including documents which change during replication.
+
+Note: this is a very, very simple validation example. It does not behave, for example, like the Apache CouchDB `validate_doc_update()` API.
diff --git a/docs/version/9.0.0/api/purge.html b/docs/version/9.0.0/api/purge.html
new file mode 100644
index 0000000000..54374a8026
--- /dev/null
+++ b/docs/version/9.0.0/api/purge.html
@@ -0,0 +1,79 @@
+{% include anchor.html edit="true" title="Purge a document rev" hash="purge" %}
+
+{% highlight js %}
+db.purge(docId, rev)
+{% endhighlight %}
+
+Purges a specific revision of a document, specified by `docId` and `rev`. `rev` must be a leaf revision.
+
+Purge permanently removes data from the database. Normal deletion with `db.remove()` does not, it only marks the document as `_deleted=true` and creates a new revision. This behaviour ensures that deletes can be replicated across databases, and deleted documents don’t get undeleted by syncing with a database that still has this document.
+
+`db.purge()` is not intended as a regular method for deleting documents, instead, it is meant as an admin function for cases where some secret was erroneously added to the database and must now be removed completely, eg. a credit card or social security number. Purge effectively puts the database in a state where the offending write never happened.
+
+{% include alert/start.html variant="info"%}
+{% markdown %}
+
+**Purge is currently only implemented in the `indexeddb` adapter**.
+
+Using Purge with any other adapter will return an error.
+
+{% endmarkdown %}
+{% include alert/end.html%}
+
+#### Example Usage:
+
+{% include code/start.html id="purge1" type="callback" %}
+{% highlight js %}
+db.purge('mydoc', '6-3a24009a9525bde9e4bfa8a99046b00d',
+ function (err, result) {
+ if (err) { return console.log(err); }
+ // handle result
+);
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="purge1" type="async" %}
+{% highlight js %}
+try {
+ const result = await db.purge('mydoc', '6-3a24009a9525bde9e4bfa8a99046b00d');
+ // handle result
+} catch (err) {
+ console.log(err);
+}
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="purge1" type="promise" %}
+{% highlight js %}
+db.purge('mydoc', '6-3a24009a9525bde9e4bfa8a99046b00d')
+ .then(function (result) {
+ // handle result
+ }).catch(function (err) {
+ console.log(err);
+ });
+{% endhighlight %}
+{% include code/end.html %}
+
+#### Example Response:
+
+{% highlight js %}
+ {
+ "ok": true,
+ "deletedRevs": [
+ "6-3a24009a9525bde9e4bfa8a99046b00d",
+ "5-df4a81cd21c75c71974d96e88a68fc2f"
+ ],
+ "documentWasRemovedCompletely": false
+ }
+{% endhighlight %}
+
+If the document has no conflicts, purging its only leaf rev deletes the document completely, and `purge()` returns `documentWasRemovedCompletely: true` in its result object.
+
+If the document does have conflicts, purging will remove the specified rev and pick a leaf from another rev branch as the winner. Fetching the doc after the purge will return the updated state of the document with the new winning revision in `_rev`.
+
+**Notes:**
+
+* The `rev` specified for purging must be a leaf, otherwise an error will be returned
+* Purges do not sync, they only apply to the database they are performed on
+* If the document has conflicts, purging a leaf will also remove its ancestors up to the next branching rev, and pick a new winning rev
+* Any attachments referenced in the specified rev will also be deleted
diff --git a/docs/version/9.0.0/api/query_database.html b/docs/version/9.0.0/api/query_database.html
new file mode 100644
index 0000000000..073ce32fe0
--- /dev/null
+++ b/docs/version/9.0.0/api/query_database.html
@@ -0,0 +1,459 @@
+{% include anchor.html edit="true" title="Map/reduce queries" hash="query_database" %}
+
+{% highlight js %}
+db.query(fun, [options], [callback])
+{% endhighlight %}
+
+Invoke a map/reduce function, which allows you to perform more complex queries on PouchDB than what you get with [allDocs()](#batch_fetch), [changes()](#changes), or [find()](#query_index). The [CouchDB documentation for map/reduce](https://docs.couchdb.org/en/stable/couchapp/views/intro.html) applies to PouchDB.
+
+Since views perform a full scan of all documents, this method may be slow, unless you first save your view in a design document. Read the [query guide](/guides/queries.html) for a good tutorial.
+
+{% include alert/start.html variant="warning"%}
+{% markdown %}
+
+**Warning: advanced API.** The map/reduce API is designed for cases that are too complex for Mango queries, which are described in
+[createIndex()](#create_index), [find()](#query_index), [listIndexes()](#list_indexes), and [deleteIndex()](#delete_index).
+
+Under the hood, Mango indexes are the same as map/reduce indexes. The Mango API is just a simplified user-facing API on top of map/reduce.
+
+{% endmarkdown %}
+{% include alert/end.html%}
+
+### Options
+
+All options default to `false` unless otherwise specified.
+
+* `fun`: Map/reduce function, which can be one of the following:
+ * A full CouchDB-style map/reduce view: `{map : ..., reduce: ...}`.
+ * A map function by itself (no reduce).
+ * The name of a view in an existing design document (e.g. `'mydesigndoc/myview'`, or `'myview'` as a shorthand for `'myview/myview'`).
+* `options.reduce`: Defaults to `true` when a reduce function is defined, or `false` otherwise. Valid values:
+ * `true` - calls the defined `reduce` function, or the `map` function if no `reduce` is defined.
+ * `false` - calls the defined `map` function.
+ * A reduce function.
+ * The string name of a built-in function: `'_sum'`, `'_count'`, or `'_stats'`.
+ * Tip: if you're not using a built-in, [you're probably doing it wrong](http://youtu.be/BKQ9kXKoHS8?t=865s).
+ * PouchDB will always call your reduce function with rereduce == false. As for CouchDB, refer to the [CouchDB documentation](https://docs.couchdb.org/en/stable/couchapp/views/intro.html).
+* `options.include_docs`: Include the document in each row in the `doc` field.
+ - `options.conflicts`: Include conflicts in the `_conflicts` field of a doc.
+ - `options.attachments`: Include attachment data.
+ - `options.binary`: Return attachment data as Blobs/Buffers, instead of as base64-encoded strings.
+* `options.startkey` & `options.endkey`: Get rows with keys in a certain range (inclusive/inclusive).
+* `options.inclusive_end`: Include rows having a key equal to the given `options.endkey`. Default: `true`.
+* `options.limit`: Maximum number of rows to return.
+* `options.skip`: Number of rows to skip before returning (warning: poor performance on IndexedDB/LevelDB!).
+* `options.descending`: Reverse the order of the output rows.
+* `options.key`: Only return rows matching this key.
+* `options.keys`: Array of keys to fetch in a single shot.
+ - Neither `startkey` nor `endkey` can be specified with this option.
+ - The rows are returned in the same order as the supplied `keys` array.
+ - The row for a deleted document will have the revision ID of the deletion, and an extra key `"deleted":true` in the `value` property.
+ - The row for a nonexistent document will just contain an `"error"` property with the value `"not_found"`.
+* `options.group`: True if you want the reduce function to group results by keys, rather than returning a single result. Defaults to `false`.
+* `options.group_level`: Number of elements in a key to group by, assuming the keys are arrays. Defaults to the full length of the array.
+* `options.stale`: One of `'ok'` or `'update_after'`. Only applies to saved views. Can be one of:
+ * unspecified (default): Returns the latest results, waiting for the view to build if necessary.
+ * `'ok'`: Returns results immediately, even if they're out-of-date.
+ * `'update_after'`: Returns results immediately, but kicks off a build afterwards.
+* `options.update_seq`: Include an `update_seq` value indicating which sequence id of the underlying database the view reflects.
+
+For details, see the [CouchDB query options documentation](https://docs.couchdb.org/en/stable/api/ddoc/views.html#get--db-_design-ddoc-_view-view).
+
+#### Example Usage:
+
+{% include code/start.html id="query1" type="callback" %}
+{% highlight js %}
+// create a design doc
+const ddoc = {
+ _id: '_design/index',
+ views: {
+ index: {
+ map: function mapFun(doc) {
+ if (doc.title) {
+ emit(doc.title);
+ }
+ }.toString()
+ }
+ }
+}
+
+// save the design doc
+db.put(ddoc, function (err) {
+ if (err && err.name !== 'conflict') {
+ return console.log(err);
+ }
+ // ignore if doc already exists
+ // find docs where title === 'Lisa Says'
+ db.query('index', {
+ key: 'Lisa Says',
+ include_docs: true
+ }, function (err, result) {
+ if (err) { return console.log(err); }
+ // handle result
+ });
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="query1" type="async" %}
+{% highlight js %}
+// create a design doc
+const ddoc = {
+ _id: '_design/index',
+ views: {
+ index: {
+ map: function mapFun(doc) {
+ if (doc.title) {
+ emit(doc.title);
+ }
+ }.toString()
+ }
+ }
+}
+
+// save the design doc
+try {
+ try {
+ await db.put(ddoc);
+ } catch (err) {
+ if (err.name !== 'conflict') {
+ throw err;
+ }
+ // ignore if doc already exists
+ }
+ // find docs where title === 'Lisa Says'
+ const result = await db.query('index', {
+ key: 'Lisa Says',
+ include_docs: true
+ });
+} catch (err) {
+ console.log(err);
+}
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="query1" type="promise" %}
+{% highlight js %}
+// create a design doc
+const ddoc = {
+ _id: '_design/index',
+ views: {
+ index: {
+ map: function mapFun(doc) {
+ if (doc.title) {
+ emit(doc.title);
+ }
+ }.toString()
+ }
+ }
+}
+
+// save the design doc
+db.put(ddoc).catch(function (err) {
+ if (err.name !== 'conflict') {
+ throw err;
+ }
+ // ignore if doc already exists
+}).then(function () {
+ // find docs where title === 'Lisa Says'
+ return db.query('index', {
+ key: 'Lisa Says',
+ include_docs: true
+ });
+}).then(function (result) {
+ // handle result
+}).catch(function (err) {
+ console.log(err);
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+#### Example Response:
+{% highlight js %}
+{
+ "offset" : 0,
+ "rows": [{
+ "id": "doc3",
+ "key": "Lisa Says",
+ "value": null,
+ "doc": {
+ "_id": "doc3",
+ "_rev": "1-z",
+ "title": "Lisa Says"
+ }
+ }],
+ "total_rows" : 4
+}
+{% endhighlight %}
+
+In the result,`total_rows` is the total number of possible results in the view. The response is very similar to that of `allDocs()`.
+
+The result may also have `update_seq` if you set `update_seq` to `true`
+
+**Note:** you can also pass in the map function instead of saving a design doc first, but this is slow because it has to do a full database scan. The following examples will use this pattern for simplicity's sake, but you should normally avoid it.
+
+#### Complex keys
+
+You can also use [complex keys](https://docs.couchdb.org/en/stable/ddocs/views/collation.html#complex-keys) for fancy ordering:
+
+{% include code/start.html id="query2" type="callback" %}
+{% highlight js %}
+function map(doc) {
+ // sort by last name, first name, and age
+ emit([doc.lastName, doc.firstName, doc.age]);
+}
+db.query(map, function (err, response) {
+ if (err) { return console.log(err); }
+ // handle result
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="query2" type="async" %}
+{% highlight js %}
+function map(doc) {
+ // sort by last name, first name, and age
+ emit([doc.lastName, doc.firstName, doc.age]);
+}
+try {
+ const result = await db.query(map);
+} catch (err) {
+ console.log(err);
+}
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="query2" type="promise" %}
+{% highlight js %}
+function map(doc) {
+ // sort by last name, first name, and age
+ emit([doc.lastName, doc.firstName, doc.age]);
+}
+db.query(map).then(function (result) {
+ // handle result
+}).catch(function (err) {
+ console.log(err);
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+#### Example Response:
+{% highlight js %}
+{
+ "offset": 0,
+ "rows": [{
+ "id" : "bowie",
+ "key" : ["Bowie", "David", 67]
+ }, {
+ "id" : "dylan",
+ "key" : ["Dylan", "Bob", 72]
+ }, {
+ "id" : "younger_dylan",
+ "key" : ["Dylan", "Jakob", 44]
+ }, {
+ "id" : "hank_the_third",
+ "key" : ["Williams", "Hank", 41]
+ }, {
+ "id" : "hank",
+ "key" : ["Williams", "Hank", 91]
+ }],
+ "total_rows": 5
+}
+{% endhighlight %}
+
+**Tips:**
+
+* The sort order is `[nulls, booleans, numbers, strings, arrays, objects]`, so `{startkey: ['Williams'], endkey: ['Williams', {}]}` would return all people with the last name `'Williams'` because objects are higher than strings. Something like `'zzzzz'` or `'\ufff0'` would also work.
+* `group_level` can be very helpful when working with complex keys. In the example above, you can use `{group_level: 1}` to group by last name, or `{group_level: 2}` to group by last and first name. (Be sure to set `{reduce: true, group: true}` as well.)
+
+#### Linked documents
+
+PouchDB fully supports [linked documents](https://docs.couchdb.org/en/stable/ddocs/views/joins.html?highlight=linked%20documents#linked-documents). Use them to join two types of documents together, by simply adding an `_id` to the emitted value:
+
+{% include code/start.html id="query3" type="callback" %}
+{% highlight js %}
+function map(doc) {
+ // join artist data to albums
+ if (doc.type === 'album') {
+ emit(doc.name, {_id : doc.artistId, albumYear : doc.year});
+ }
+}
+db.query(map, {include_docs : true}, function (err, response) {
+ if (err) { return console.log(err); }
+ // handle result
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="query3" type="async" %}
+{% highlight js %}
+function map(doc) {
+ // join artist data to albums
+ if (doc.type === 'album') {
+ emit(doc.name, {_id : doc.artistId, albumYear : doc.year});
+ }
+}
+try {
+ const result = await db.query(map, {include_docs : true});
+} catch (err) {
+ console.log(err);
+}
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="query3" type="promise" %}
+{% highlight js %}
+function map(doc) {
+ // join artist data to albums
+ if (doc.type === 'album') {
+ emit(doc.name, {_id : doc.artistId, albumYear : doc.year});
+ }
+}
+db.query(map, {include_docs : true}).then(function (result) {
+ // handle result
+}).catch(function (err) {
+ console.log(err);
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+#### Example response:
+
+{% highlight js %}
+{
+ "offset": 0,
+ "rows": [
+ {
+ "doc": {
+ "_id": "bowie",
+ "_rev": "1-fdb234b78904a5c8293f2acf4be70d44",
+ "age": 67,
+ "firstName": "David",
+ "lastName": "Bowie"
+ },
+ "id": "album_hunkeydory",
+ "key": "Hunky Dory",
+ "value": {
+ "_id": "bowie",
+ "albumYear": 1971
+ }
+ },
+ {
+ "doc": {
+ "_id": "bowie",
+ "_rev": "1-fdb234b78904a5c8293f2acf4be70d44",
+ "age": 67,
+ "firstName": "David",
+ "lastName": "Bowie"
+ },
+ "id": "album_low",
+ "key": "Low",
+ "value": {
+ "_id": "bowie",
+ "albumYear": 1977
+ }
+ },
+ {
+ "doc": {
+ "_id": "bowie",
+ "_rev": "1-fdb234b78904a5c8293f2acf4be70d44",
+ "age": 67,
+ "firstName": "David",
+ "lastName": "Bowie"
+ },
+ "id": "album_spaceoddity",
+ "key": "Space Oddity",
+ "value": {
+ "_id": "bowie",
+ "albumYear": 1969
+ }
+ }
+ ],
+ "total_rows": 3
+}
+{% endhighlight %}
+
+#### Closures
+
+If you pass a function to `db.query` and give it the `emit` function as the second argument, then you can use a closure. (Since PouchDB has to use `eval()` to bind `emit`.)
+
+{% include code/start.html id="query4" type="callback" %}
+{% highlight js %}
+// BAD! will throw error
+const myId = 'foo';
+db.query(function(doc) {
+ if (doc._id === myId) {
+ emit(doc);
+ }
+}, function(err, results) {
+ // you'll get an error here
+});
+
+// will be fine
+const myId = 'foo';
+db.query(function(doc, emit) {
+ if (doc._id === myId) {
+ emit(doc);
+ }
+}, function(err, results) {
+ if (err) { return console.log(err); }
+ // handle result
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="query4" type="async" %}
+{% highlight js %}
+// BAD! will throw error
+const myId = 'foo';
+try {
+ const result = await db.query(function(doc) {
+ if (doc._id === myId) {
+ emit(doc);
+ }
+ });
+} catch (err) {
+ // you'll get an error here
+}
+
+// will be fine
+const myId = 'foo';
+try {
+ const result = await db.query(function(doc, emit) {
+ if (doc._id === myId) {
+ emit(doc);
+ }
+ });
+} catch (err) {
+ console.log(err);
+}
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="query4" type="promise" %}
+{% highlight js %}
+// BAD! will throw error
+const myId = 'foo';
+db.query(function(doc) {
+ if (doc._id === myId) {
+ emit(doc);
+ }
+}).catch(function (err) {
+ // you'll get an error here
+}
+
+// will be fine
+const myId = 'foo';
+db.query(function(doc, emit) {
+ if (doc._id === myId) {
+ emit(doc);
+ }
+}).then(function (result) {
+ // handle result
+}).catch(function (err) {
+ console.log(err);
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+Note that closures are only supported by local databases with temporary views. So if you are using closures, then you must use the slower method that requires a full database scan.
diff --git a/docs/version/9.0.0/api/query_index.html b/docs/version/9.0.0/api/query_index.html
new file mode 100644
index 0000000000..7ea7ca8b8a
--- /dev/null
+++ b/docs/version/9.0.0/api/query_index.html
@@ -0,0 +1,338 @@
+{% include anchor.html edit="true" title="Query index" hash="query_index" %}
+
+{% highlight js %}
+db.find(request [, callback])
+{% endhighlight %}
+
+Query an index and return the list of documents that match the request.
+
+{% include alert/start.html variant="info"%}
+{% markdown %}
+
+**pouchdb-find plugin needed:** This API requires the `pouchdb-find` plugin. See
+[Mango queries](/guides/mango-queries.html) for installation instructions.
+
+{% endmarkdown %}
+{% include alert/end.html%}
+
+#### Example Usage:
+
+{% include code/start.html id="query_idx" type="callback" %}
+{% highlight js %}
+db.find({
+ selector: {name: 'Mario'},
+ fields: ['_id', 'name'],
+ sort: ['name']
+}, function (err, result) {
+ if (err) { return console.log(err); }
+ // handle result
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="query_idx" type="async" %}
+{% highlight js %}
+try {
+ const result = await db.find({
+ selector: {name: 'Mario'},
+ fields: ['_id', 'name'],
+ sort: ['name']
+ });
+} catch (err) {
+ console.log(err);
+}
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="query_idx" type="promise" %}
+{% highlight js %}
+db.find({
+ selector: {name: 'Mario'},
+ fields: ['_id', 'name'],
+ sort: ['name']
+}).then(function (result) {
+ // handle result
+}).catch(function (err) {
+ console.log(err);
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+#### Example Response:
+
+{% highlight js %}
+{
+ "docs": [
+ {
+ "_id": "mario",
+ "name": "Mario"
+ }
+ ]
+}
+{% endhighlight %}
+
+The above is a simple example. For an in-depth tutorial, please refer to
+[the Mango guide](/guides/mango-queries.html).
+
+### Options
+
+* `selector` Defines a selector to filter the results. Required.
+ * `$lt` Match fields "less than" this one.
+ * `$gt` Match fields "greater than" this one.
+ * `$lte` Match fields "less than or equal to" this one.
+ * `$gte` Match fields "greater than or equal to" this one.
+ * `$eq` Match fields equal to this one.
+ * `$ne` Match fields not equal to this one.
+ * `$exists` True if the field should exist, false otherwise.
+ * `$type` One of: "null", "boolean", "number", "string", "array", or "object".
+ * `$in` The document field must exist in the list provided.
+ * `$and` Matches if all the selectors in the array match.
+ * `$nin` The document field must not exist in the list provided.
+ * `$all` Matches an array value if it contains all the elements of the argument array.
+ * `$size` Special condition to match the length of an array field in a document.
+ * `$or` Matches if any of the selectors in the array match. All selectors must use the same index.
+ * `$nor` Matches if none of the selectors in the array match.
+ * `$not` Matches if the given selector does not match.
+ * `$mod` Matches documents where (field % Divisor == Remainder) is true, and only when the document field is an integer.
+ * `$regex` A regular expression pattern to match against the document field.
+ * `$elemMatch` Matches all documents that contain an array field with at least one element that matches all the specified query criteria.
+* `fields` (Optional) Defines a list of fields that you want to receive. If omitted, you get the full documents.
+* `sort` (Optional) Defines a list of fields defining how you want to sort. Note that sorted fields also have to be selected in the `selector`.
+* `limit` (Optional) Maximum number of documents to return. Default is `25` when connected to a local database.
+* `skip` (Optional) Number of docs to skip before returning.
+* `use_index` (Optional) Set which index to use for the query. It can be "design-doc-name" or "['design-doc-name', 'name']".
+
+If there's no index that matches your `selector`/`sort`, then this method will issue a warning:
+
+{% highlight js %}
+{
+ "docs": [ /* ... */ ],
+ "warning": "No matching index found, create an index to optimize query time."
+}
+{% endhighlight %}
+
+The best index will be chosen automatically.
+
+See the [CouchDB `_find` documentation](https://docs.couchdb.org/en/stable/api/database/find.html) for more details on
+selectors and the Mango query language.
+
+### More examples
+
+Use `$eq` for "equals":
+
+{% include code/start.html id="query_idx2" type="callback" %}
+{% highlight js %}
+db.find({
+ selector: {name: {$eq: 'Mario'}}
+}, function (err, result) {
+ if (err) { return console.log(err); }
+ // handle result
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="query_idx2" type="async" %}
+{% highlight js %}
+try {
+ const result = await db.find({
+ selector: {name: {$eq: 'Mario'}}
+ });
+} catch (err) {
+ console.log(err);
+}
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="query_idx2" type="promise" %}
+{% highlight js %}
+db.find({
+ selector: {name: {$eq: 'Mario'}}
+}).then(function (result) {
+ // handle result
+}).catch(function (err) {
+ console.log(err);
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+This is equivalent to:
+
+{% include code/start.html id="query_idx3" type="callback" %}
+{% highlight js %}
+db.find({
+ selector: {name: 'Mario'}
+}, function (err, result) {
+ if (err) { return console.log(err); }
+ // handle result
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="query_idx3" type="async" %}
+{% highlight js %}
+try {
+ const result = await db.find({
+ selector: {name: 'Mario'}
+ });
+} catch (err) {
+ console.log(err);
+}
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="query_idx3" type="promise" %}
+{% highlight js %}
+db.find({
+ selector: {name: 'Mario'}
+}).then(function (result) {
+ // handle result
+}).catch(function (err) {
+ console.log(err);
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+You can also do selections on multiple fields. For instance, to
+find all docs where `series` is `'Mario'` and `debut` is greater than `1990`:
+
+{% include code/start.html id="query_idx4" type="callback" %}
+{% highlight js %}
+db.find({
+ selector: {
+ series: 'Mario',
+ debut: { $gt: 1990 }
+ }
+}, function (err, result) {
+ if (err) { return console.log(err); }
+ // handle result
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="query_idx4" type="async" %}
+{% highlight js %}
+try {
+ const result = await db.find({
+ selector: {
+ series: 'Mario',
+ debut: { $gt: 1990 }
+ }
+ });
+} catch (err) {
+ console.log(err);
+}
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="query_idx4" type="promise" %}
+{% highlight js %}
+db.find({
+ selector: {
+ series: 'Mario',
+ debut: { $gt: 1990 }
+ }
+}).then(function (result) {
+ // handle result
+}).catch(function (err) {
+ console.log(err);
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+This is equivalent to:
+
+{% include code/start.html id="query_idx5" type="callback" %}
+{% highlight js %}
+db.find({
+ selector: {
+ $and: [
+ { series: 'Mario' },
+ { debut: { $gt: 1990 } }
+ ]
+ }
+}, function (err, result) {
+ if (err) { return console.log(err); }
+ // handle result
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="query_idx5" type="async" %}
+{% highlight js %}
+try {
+ const result = await db.find({
+ selector: {
+ $and: [
+ { series: 'Mario' },
+ { debut: { $gt: 1990 } }
+ ]
+ }
+ });
+} catch (err) {
+ console.log(err);
+}
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="query_idx5" type="promise" %}
+{% highlight js %}
+db.find({
+ selector: {
+ $and: [
+ { series: 'Mario' },
+ { debut: { $gt: 1990 } }
+ ]
+ }
+}).then(function (result) {
+ // handle result
+}).catch(function (err) {
+ console.log(err);
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+You can also sort the returned documents. For instance, to find all docs sorted by `debut` descending:
+
+{% include code/start.html id="query_idx6" type="callback" %}
+{% highlight js %}
+db.find({
+ selector: {
+ debut: {'$gte': null}
+ },
+ sort: [{debut: 'desc'}]
+}, function (err, result) {
+ if (err) { return console.log(err); }
+ // handle result
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="query_idx6" type="async" %}
+{% highlight js %}
+try {
+ const result = await db.find({
+ selector: {
+ debut: {'$gte': null}
+ },
+ sort: [{debut: 'desc'}]
+ });
+} catch (err) {
+ console.log(err);
+}
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="query_idx6" type="promise" %}
+{% highlight js %}
+db.find({
+ selector: {
+ debut: {'$gte': null}
+ },
+ sort: [{debut: 'desc'}]
+}).then(function (result) {
+ // handle result
+}).catch(function (err) {
+ console.log(err);
+});
+{% endhighlight %}
+{% include code/end.html %}
diff --git a/docs/version/9.0.0/api/replication.html b/docs/version/9.0.0/api/replication.html
new file mode 100644
index 0000000000..7ee4284e07
--- /dev/null
+++ b/docs/version/9.0.0/api/replication.html
@@ -0,0 +1,322 @@
+{% include anchor.html edit="true" title="Replicate a database" hash="replication" %}
+
+{% highlight js %}
+PouchDB.replicate(source, target, [options])
+{% endhighlight %}
+
+Replicate data from `source` to `target`. Both the `source` and `target` can be a PouchDB instance or a string representing a CouchDB database URL or the name of a local PouchDB database. If `options.live` is `true`, then this will track future changes and also replicate them automatically. This method returns an object with the method `cancel()`, which you call if you want to cancel live replication.
+
+Replication is an [event emitter][] like [changes()](#changes) and emits the `'complete'`, `'active'`, `'paused'`, `'change'`, `'denied'`, `'error'` and `'checkpoint'` events.
+
+### Options
+
+All options default to `false` unless otherwise specified.
+
+* `options.live`: If `true`, starts subscribing to future changes in the `source` database and continue replicating them.
+* `options.retry`: If `true` will attempt to retry replications in the case of failure (due to being offline), using a backoff algorithm that retries at longer and longer intervals until a connection is re-established, with a maximum delay of 10 minutes. Only applicable if `options.live` is also `true`.
+
+**Filtering Options:**
+
+* `options.filter`: Reference a filter function from a design document to selectively get updates. To use a view function, pass `_view` here and provide a reference to the view function in `options.view`. See [filtered replication](#filtered-replication) for details.
+* `options.doc_ids`: Only show changes for docs with these ids (array of strings).
+* `options.query_params`: Object containing properties that are passed to the filter function, e.g. `{"foo":"bar"}`, where `"bar"` will be available in the filter function as `params.query.foo`. To access the `params`, define your filter function like `function (doc, params) {/* ... */}`.
+* `options.view`: Specify a view function (e.g. `'design_doc_name/view_name'` or `'view_name'` as shorthand for `'view_name/view_name'`) to act as a filter. Documents counted as "passed" for a view filter if a map function emits at least one record for them. **Note**: `options.filter` must be set to `'_view'` for this option to work.
+* `options.selector`: Filter using a query/pouchdb-find [selector](https://docs.couchdb.org/en/stable/api/database/find.html#find-selectors).
+
+**Advanced Options:**
+
+* `options.since`: Replicate changes after the given sequence number.
+* `options.heartbeat`: Configure the heartbeat supported by CouchDB which keeps the change connection alive.
+* `options.timeout`: Request timeout (in milliseconds).
+* `options.batch_size`: Number of change feed items to process at a time. Defaults to 100. This affects the number of docs and attachments held in memory and the number sent at a time to the target server. You may need to adjust downward if targeting devices with low amounts of memory (e.g. phones) or if the documents and/or attachments are large in size or if there are many conflicted revisions. If your documents are small in size, then increasing this number will probably speed replication up.
+* `options.batches_limit`: Number of batches to process at a time. Defaults to 10. This (along with `batch_size`) controls how many docs are kept in memory at a time, so the maximum docs in memory at once would equal `batch_size` × `batches_limit`.
+* `options.back_off_function`: backoff function to be used in `retry` replication. This is a function that takes the current backoff as input (or 0 the first time) and returns a new backoff in milliseconds. You can use this to tweak when and how replication will try to reconnect to a remote database when the user goes offline. Defaults to a function that chooses a random backoff between 0 and 2 seconds and doubles every time it fails to connect. The default delay will never exceed 10 minutes. (See [Customizing retry replication](#customizing-retry-replication) below.)
+* `options.checkpoint`: Can be used if you want to disable checkpoints on the source, target, or both. Setting this option to `false` will prevent writing checkpoints on both source and target. Setting it to `source` will only write checkpoints on the source. Setting it to `target` will only write checkpoints on the target.
+* `options.style`: Specifies whether all revisions of a document including conflicts and deleted former conflicts (`all_docs`) or only the winning revision (`main_only`) should be replicated. This option is passed to the `changes` endpoint of the replication source. Defaults to `all_docs`.
+
+#### Example Usage:
+
+{% highlight js %}
+const rep = PouchDB.replicate('mydb', 'http://localhost:5984/mydb', {
+ live: true,
+ retry: true
+}).on('change', function (info) {
+ // handle change
+}).on('paused', function (err) {
+ // replication paused (e.g. replication up to date, user went offline)
+}).on('active', function () {
+ // replicate resumed (e.g. new changes replicating, user went back online)
+}).on('denied', function (err) {
+ // a document failed to replicate (e.g. due to permissions)
+}).on('complete', function (info) {
+ // handle complete
+}).on('error', function (err) {
+ // handle error
+});
+
+rep.cancel(); // whenever you want to cancel
+{% endhighlight %}
+
+There are also shorthands for replication given existing PouchDB objects. These behave the same as `PouchDB.replicate()`:
+
+{% highlight js %}
+db.replicate.to(remoteDB, [options]);
+// or
+db.replicate.from(remoteDB, [options]);
+{% endhighlight %}
+
+The `remoteDB` can either be a string or a `PouchDB` object. If you have a fetch override on a remote database, you will want to use `PouchDB` objects instead of strings, so that the options are used.
+
+#### Replication events
+
+* __`change`__ (`info`) - This event fires when the replication has written a new document. `info` will contain details about the change. `info.docs` will contain the docs involved in that change. See below for an example response.
+* __`complete`__ (`info`) - This event fires when replication is completed or cancelled. In a live replication, only cancelling the replication should trigger this event. `info` will contain details about the replication. See below for an example response.
+* __`paused`__ (`err`) - This event fires when the replication is paused, either because a live replication is waiting for changes, or replication has temporarily failed, with `err`, and is attempting to resume.
+* __`active`__ - This event fires when the replication starts actively processing changes; e.g. when it recovers from an error or new changes are available.
+* __`denied`__ (`err`) - This event fires if a document failed to replicate due to validation or authorization errors.
+* __`error`__ (`err`) - This event is fired when the replication is stopped due to an unrecoverable failure. If `retry` is `false`, this will also fire when the user goes offline or another network error occurs (so you can handle retries yourself, if you want).
+* __`checkpoint`__ - This event exposes information about the internal steps of the replication process. It includes one of the fields `pending_batch`, `start_next_batch`, `revs_diff` or `checkpoint`.
+
+#### Single-shot
+
+As with [changes()](#changes), you can also omit `live`, in which case you can use `replicate()` in the callback/promise style and it will be treated as a single-shot operation.
+
+{% include code/start.html id="replication1" type="callback" %}
+{% highlight js %}
+db.replicate.to(remote, function (err, result) {
+ if (err) { return console.log(err); }
+ // handle 'completed' result
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="replication1" type="async" %}
+{% highlight js %}
+try {
+ const result = await db.replicate.to(remote);
+} catch (err) {
+ console.log(err);
+}
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="replication1" type="promise" %}
+{% highlight js %}
+db.replicate.to(remote).then(function (result) {
+ // handle 'completed' result
+}).catch(function (err) {
+ console.log(err);
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+For non-live replications, the returned object is also an event emitter as well as a promise, and you can use all the events described above (except for `'paused'` and `'active'`, which only apply to `retry` replications).
+
+#### Example Response:
+
+Example response in the `'change'` listener:
+
+{% highlight js %}
+{
+ "doc_write_failures": 0,
+ "docs_read": 1,
+ "docs_written": 1,
+ "errors": [],
+ "last_seq": 1,
+ "ok": true,
+ "start_time": "Fri May 16 2014 18:23:12 GMT-0700 (PDT)",
+ "docs": [
+ { _id: 'docId',
+ _rev: '1-e798a1a7eb4247b399dbfec84ca699d4',
+ and: 'data' }
+ ]
+}
+{% endhighlight %}
+
+Example response in the `'complete'` listener:
+
+{% highlight js %}
+{
+ "doc_write_failures": 0,
+ "docs_read": 2,
+ "docs_written": 2,
+ "end_time": "Fri May 16 2014 18:26:00 GMT-0700 (PDT)",
+ "errors": [],
+ "last_seq": 2,
+ "ok": true,
+ "start_time": "Fri May 16 2014 18:26:00 GMT-0700 (PDT)",
+ "status": "complete"
+}
+{% endhighlight %}
+
+Note that replication is supported for both local and remote databases. So you can replicate from local to local or from remote to remote.
+
+However, if you replicate from remote to remote, then the changes will flow through PouchDB. If you want to trigger a server-initiated replication, please use regular ajax to POST to the CouchDB `_replicate` endpoint, as described [in the CouchDB docs](https://docs.couchdb.org/en/stable/api/server/common.html#api-server-replicate).
+
+#### Filtered replication
+
+As with [changes()](#changes), you can filter from the source database using:
+
+* an ad-hoc `filter` function
+* an array of `doc_ids`
+* a `filter` function inside of a design document
+* a `filter` function inside of a design document, with `query_params`
+* a `view` function inside of a design document
+
+If you are replicating from a remote CouchDB, then the first method will run client-side, whereas the last four will filter on the server side. Therefore the last four should be preferred, especially if the database is large, because you want to send as few documents over the wire as possible.
+
+You should also beware trying to use filtered replication to enforce security, e.g. to partition a database per user. A better strategy is the ["one database per user" method](https://github.com/nolanlawson/pouchdb-authentication#couchdb-authentication-recipes).
+
+{% include alert/start.html variant="warning" %}
+
+{% markdown %}
+
+**Deleting filtered docs**: When you use filtered replication, you should avoid using `remove()` to delete documents, because that removes all their fields as well, which means they might not pass the filter function anymore, causing the deleted revision to not be replicated. Instead, set the `doc._deleted` flag to `true` and then use `put()` or `bulkDocs()`.
+
+{% endmarkdown %}
+
+{% include alert/end.html %}
+
+#### Filtering examples
+
+In these examples, we'll work with some mammals. Let's imagine our docs are:
+
+{% highlight js %}
+[
+ {_id: 'a', name: 'Kangaroo', type: 'marsupial'},
+ {_id: 'b', name: 'Koala', type: 'marsupial'},
+ {_id: 'c', name: 'Platypus', type: 'monotreme'}
+]
+{% endhighlight %}
+
+Here are 5 examples using the 5 different systems.
+
+**Example 1: Ad-hoc `filter` function**
+
+*Warning*: this runs client-side, if you are replicating from a remote database.
+
+Filter by `type === 'marsupial'`:
+
+{% highlight js %}
+remote.replicate.to(local, {
+ filter: function (doc) {
+ return doc.type === 'marsupial';
+ }
+});
+{% endhighlight %}
+
+**Example 2: Array of `doc_ids`**
+
+Filter documents with `_id`s `['a', 'c']`.
+
+{% highlight js %}
+remote.replicate.to(local, {
+ doc_ids: ['a', 'c']
+});
+{% endhighlight %}
+
+**Example 3: `filter` function inside of a design document**
+
+First `put()` a design document in the remote database:
+
+{% highlight js %}
+{
+ _id: '_design/mydesign',
+ filters: {
+ myfilter: function (doc) {
+ return doc.type === 'marsupial';
+ }.toString()
+ }
+}
+{% endhighlight %}
+
+Then filter by `type === 'marsupial'`:
+
+{% highlight js %}
+remote.replicate.to(local, {
+ filter: 'mydesign/myfilter'
+});
+{% endhighlight %}
+
+**Example 4: `filter` function inside of a design document, with `query_params`**
+
+This is the most powerful way to filter, because it allows you to pass in arbitrary options to your filter function.
+
+First `put()` a design document in the remote database:
+
+{% highlight js %}
+{
+ _id: '_design/mydesign',
+ filters: {
+ myfilter: function (doc, req) {
+ return doc.type === req.query.type;
+ }.toString()
+ }
+}
+{% endhighlight %}
+
+Then filter by `type === 'marsupial'`:
+
+{% highlight js %}
+remote.replicate.to(local, {
+ filter: 'mydesign/myfilter',
+ query_params: {type: 'marsupial'}
+});
+{% endhighlight %}
+
+**Example 5: `view` function inside of a design document**
+
+This doesn't really offer any advantages compared to the previous two methods, unless you are already using a `view` for map/reduce queries, and you want to reuse it.
+
+Any documents that `emit()` anything will be considered to have passed this filter method.
+
+First `put()` a design document in the remote database:
+
+{% highlight js %}
+{
+ _id: '_design/mydesign',
+ views: {
+ myview: {
+ map: function(doc) {
+ if (doc.type === 'marsupial') {
+ emit(doc._id);
+ }
+ }.toString()
+ }
+ }
+}
+{% endhighlight %}
+
+Then filter by `type === 'marsupial'`:
+
+{% highlight js %}
+remote.replicate.to(local, {
+ filter: '_view',
+ view: 'mydesign/myview'
+});
+{% endhighlight %}
+
+#### Customizing retry replication
+
+During `retry` replication, you can customize the backoff function that determines how long to wait before reconnecting when the user goes offline.
+
+Here's a simple backoff function that starts at 1000 milliseconds and triples it every time a remote request fails:
+
+{% highlight js %}
+
+db.replicate.to(remote, {
+ live: true,
+ retry: true,
+ back_off_function: function (delay) {
+ if (delay === 0) {
+ return 1000;
+ }
+ return delay * 3;
+ }
+});
+
+{% endhighlight %}
+
+The first time a request fails, this function will receive 0 as input. The next time it fails, 1000 will be passed in, then 3000, then 9000, etc. When the user comes back online, the `delay` goes back to 0.
+
+By default, PouchDB uses a backoff function that chooses a random starting number between 0 and 2000 milliseconds and will roughly double every time, with some randomness to prevent client requests from occurring simultaneously.
diff --git a/docs/version/9.0.0/api/revisions_diff.html b/docs/version/9.0.0/api/revisions_diff.html
new file mode 100644
index 0000000000..9bd6c45607
--- /dev/null
+++ b/docs/version/9.0.0/api/revisions_diff.html
@@ -0,0 +1,65 @@
+{% include anchor.html edit="true" title="Document revisions diff" hash="revisions_diff" %}
+
+{% highlight js %}
+db.revsDiff(diff, [callback])
+{% endhighlight %}
+
+Given a set of document/revision IDs, returns the subset of those that do not correspond
+to revisions stored in the database. Primarily used in replication.
+
+#### Example Usage:
+
+{% include code/start.html id="revsdiff1" type="callback" %}
+{% highlight js %}
+db.revsDiff({
+ myDoc1: [
+ "1-b2e54331db828310f3c772d6e042ac9c",
+ "2-3a24009a9525bde9e4bfa8a99046b00d"
+ ]
+}, function (err, result) {
+ if (err) { return console.log(err); }
+ // handle result
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="revsdiff1" type="async" %}
+{% highlight js %}
+try {
+ const result = await db.revsDiff({
+ myDoc1: [
+ "1-b2e54331db828310f3c772d6e042ac9c",
+ "2-3a24009a9525bde9e4bfa8a99046b00d"
+ ]
+ });
+} catch (err) {
+ console.log(err);
+}
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="revsdiff1" type="promise" %}
+{% highlight js %}
+db.revsDiff({
+ myDoc1: [
+ "1-b2e54331db828310f3c772d6e042ac9c",
+ "2-3a24009a9525bde9e4bfa8a99046b00d"
+ ]
+}).then(function (result) {
+ // handle result
+}).catch(function (err) {
+ console.log(err);
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+#### Example Response:
+{% highlight js %}
+{
+ "myDoc1": {
+ "missing": ["2-3a24009a9525bde9e4bfa8a99046b00d"]
+ }
+}
+{% endhighlight %}
+
+
diff --git a/docs/version/9.0.0/api/save_attachment.html b/docs/version/9.0.0/api/save_attachment.html
new file mode 100644
index 0000000000..3f2ac38207
--- /dev/null
+++ b/docs/version/9.0.0/api/save_attachment.html
@@ -0,0 +1,349 @@
+{% include anchor.html edit="true" title="Save an attachment" hash="save_attachment" %}
+
+{% highlight js %}
+db.putAttachment(docId, attachmentId, [rev], attachment, type, [callback]);
+{% endhighlight %}
+
+Attaches a binary object to a document.
+
+This method will update an existing document to add the attachment, so it requires a `rev` if the document already exists. If the document doesn't already exist, then this method will create an empty document containing the attachment.
+
+What's the point of attachments? If you're dealing with large binary data (such as PNGs), you may incur a performance or storage penalty if you naïvely include them as base64- or hex-encoded strings inside your documents. But if you insert the binary data as an attachment, then PouchDB will attempt to store it in [the most efficient way possible]({{ site.baseurl }}/faq.html#data_types).
+
+For details, see the [CouchDB documentation on attachments](https://docs.couchdb.org/en/stable/api/document/attachments.html#put--db-docid-attname).
+
+#### Example Usage:
+
+{% include code/start.html id="attach1" type="callback" %}
+{% highlight js %}
+const attachment = new Blob(['Is there life on Mars?'], {type: 'text/plain'});
+db.putAttachment('doc', 'att.txt', attachment, 'text/plain', function(err, res) {
+ if (err) { return console.log(err); }
+ // handle result
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="attach1" type="async" %}
+{% highlight js %}
+try {
+ const attachment = new Blob(['Is there life on Mars?'], {type: 'text/plain'});
+ const result = await db.putAttachment('doc', 'att.txt', attachment, 'text/plain');
+} catch (err) {
+ console.log(err);
+}
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="attach1" type="promise" %}
+{% highlight js %}
+const attachment = new Blob(['Is there life on Mars?'], {type: 'text/plain'});
+db.putAttachment('doc', 'att.txt', attachment, 'text/plain').then(function (result) {
+ // handle result
+}).catch(function (err) {
+ console.log(err);
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+
+#### Example Response:
+{% highlight js %}
+{
+ "ok": true,
+ "id": "doc",
+ "rev": "2-068E73F5B44FEC987B51354DFC772891"
+}
+{% endhighlight %}
+
+Within Node, you must use a `Buffer` instead of a `Blob`:
+
+{% highlight js %}
+const attachment = new Buffer('Is there life on Mars?');
+{% endhighlight %}
+
+For details, see the [Mozilla docs on `Blob`](https://developer.mozilla.org/en-US/docs/Web/API/Blob) or the [Node docs on `Buffer`](http://nodejs.org/api/buffer.html).
+
+If you need a shim for older browsers that don't support the `Blob` constructor, or you want some convenience methods for Blobs, you can use [blob-util](https://github.com/nolanlawson/blob-util).
+
+#### Save a base64 attachment
+
+If you supply a string instead of a `Blob`/`Buffer`, then it will be assumed to be a base64-encoded string, and will be processed accordingly:
+
+{% include code/start.html id="attach3" type="callback" %}
+{% highlight js %}
+const attachment =
+ "TGVnZW5kYXJ5IGhlYXJ0cywgdGVhciB1cyBhbGwgYXBhcnQKTWFrZS" +
+ "BvdXIgZW1vdGlvbnMgYmxlZWQsIGNyeWluZyBvdXQgaW4gbmVlZA==";
+db.putAttachment('doc', 'att.txt', attachment, 'text/plain', function(err, res) {
+ if (err) { return console.log(err); }
+ // handle result
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="attach3" type="async" %}
+{% highlight js %}
+try {
+ const attachment =
+ "TGVnZW5kYXJ5IGhlYXJ0cywgdGVhciB1cyBhbGwgYXBhcnQKTWFrZS" +
+ "BvdXIgZW1vdGlvbnMgYmxlZWQsIGNyeWluZyBvdXQgaW4gbmVlZA==";
+ const result = await db.putAttachment('doc', 'att.txt', attachment, 'text/plain');
+} catch (err) {
+ console.log(err);
+}
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="attach3" type="promise" %}
+{% highlight js %}
+const attachment =
+ "TGVnZW5kYXJ5IGhlYXJ0cywgdGVhciB1cyBhbGwgYXBhcnQKTWFrZS" +
+ "BvdXIgZW1vdGlvbnMgYmxlZWQsIGNyeWluZyBvdXQgaW4gbmVlZA==";
+db.putAttachment('doc', 'att.txt', attachment, 'text/plain').then(function (result) {
+ // handle result
+}).catch(function (err) {
+ console.log(err);
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+
+#### Save an inline attachment
+
+You can also inline attachments inside the document. The attachment data may be supplied as a base64-encoded string with the `content_type`:
+
+{% include code/start.html id="attach2" type="callback" %}
+{% highlight js %}
+const doc = {
+ "_id": "doc",
+ "title": "Legendary Hearts",
+ "_attachments": {
+ "att.txt": {
+ "content_type": "text/plain",
+ "data": "TGVnZW5kYXJ5IGhlYXJ0cywgdGVhciB1cyBhbGwgYXBhcnQKTWFrZS" +
+ "BvdXIgZW1vdGlvbnMgYmxlZWQsIGNyeWluZyBvdXQgaW4gbmVlZA=="
+ }
+ }
+};
+db.put(doc, function (err, result) {
+ if (err) { return console.log(err); }
+ // handle result
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="attach2" type="async" %}
+{% highlight js %}
+try {
+ const doc = {
+ "_id": "doc",
+ "title": "Legendary Hearts",
+ "_attachments": {
+ "att.txt": {
+ "content_type": "text/plain",
+ "data": "TGVnZW5kYXJ5IGhlYXJ0cywgdGVhciB1cyBhbGwgYXBhcnQKTWFrZS" +
+ "BvdXIgZW1vdGlvbnMgYmxlZWQsIGNyeWluZyBvdXQgaW4gbmVlZA=="
+ }
+ }
+ };
+ const result = await db.put(doc);
+} catch (err) {
+ console.log(err);
+}
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="attach2" type="promise" %}
+{% highlight js %}
+const doc = {
+ "_id": "doc",
+ "title": "Legendary Hearts",
+ "_attachments": {
+ "att.txt": {
+ "content_type": "text/plain",
+ "data": "TGVnZW5kYXJ5IGhlYXJ0cywgdGVhciB1cyBhbGwgYXBhcnQKTWFrZS" +
+ "BvdXIgZW1vdGlvbnMgYmxlZWQsIGNyeWluZyBvdXQgaW4gbmVlZA=="
+ }
+ }
+};
+db.put(doc).then(function (result) {
+ // handle result
+}).catch(function (err) {
+ console.log(err);
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+#### Save an inline Blob/Buffer attachment
+
+You can also inline `Blob`s/`Buffer`s:
+
+{% include code/start.html id="attach4" type="callback" %}
+{% highlight js %}
+const doc = {
+ "_id": "doc",
+ "title": "Legendary Hearts",
+ "_attachments": {
+ "att.txt": {
+ "content_type": "text/plain",
+ "data": new Blob(['Is there life on Mars?'], {type: 'text/plain'})
+ }
+ }
+};
+db.put(doc, function (err, result) {
+ if (err) { return console.log(err); }
+ // handle result
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="attach4" type="async" %}
+{% highlight js %}
+try {
+ const doc = {
+ "_id": "doc",
+ "title": "Legendary Hearts",
+ "_attachments": {
+ "att.txt": {
+ "content_type": "text/plain",
+ "data": new Blob(['Is there life on Mars?'], {type: 'text/plain'})
+ }
+ }
+ };
+ const result = await db.put(doc);
+} catch (err) {
+ console.log(err);
+}
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="attach4" type="promise" %}
+{% highlight js %}
+const doc = {
+ "_id": "doc",
+ "title": "Legendary Hearts",
+ "_attachments": {
+ "att.txt": {
+ "content_type": "text/plain",
+ "data": new Blob(['Is there life on Mars?'], {type: 'text/plain'})
+ }
+ }
+};
+db.put(doc).then(function (result) {
+ // handle result
+}).catch(function (err) {
+ console.log(err);
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+#### Save many attachments at once
+
+The inline approach allows you to save multiple attachments to the same document in a single shot:
+
+{% include code/start.html id="attach5" type="callback" %}
+{% highlight js %}
+const doc = {
+ "_id": "doc",
+ "title": "Legendary Hearts",
+ "_attachments": {
+ "att.txt": {
+ "content_type": "text/plain",
+ "data": new Blob(
+ ["And she's hooked to the silver screen"],
+ {type: 'text/plain'})
+ },
+ "att2.txt": {
+ "content_type": "text/plain",
+ "data": new Blob(
+ ["But the film is a saddening bore"],
+ {type: 'text/plain'})
+ },
+ "att3.txt": {
+ "content_type": "text/plain",
+ "data": new Blob(
+ ["For she's lived it ten times or more"],
+ {type: 'text/plain'})
+ }
+ }
+};
+db.put(doc, function (err, result) {
+ if (err) { return console.log(err); }
+ // handle result
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="attach5" type="async" %}
+{% highlight js %}
+try {
+ const doc = {
+ "_id": "doc",
+ "title": "Legendary Hearts",
+ "_attachments": {
+ "att.txt": {
+ "content_type": "text/plain",
+ "data": new Blob(
+ ["And she's hooked to the silver screen"],
+ {type: 'text/plain'})
+ },
+ "att2.txt": {
+ "content_type": "text/plain",
+ "data": new Blob(
+ ["But the film is a saddening bore"],
+ {type: 'text/plain'})
+ },
+ "att3.txt": {
+ "content_type": "text/plain",
+ "data": new Blob(
+ ["For she's lived it ten times or more"],
+ {type: 'text/plain'})
+ }
+ }
+ };
+ const result = await db.put(doc);
+} catch (err) {
+ console.log(err);
+}
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="attach5" type="promise" %}
+{% highlight js %}
+const doc = {
+ "_id": "doc",
+ "title": "Legendary Hearts",
+ "_attachments": {
+ "att.txt": {
+ "content_type": "text/plain",
+ "data": new Blob(
+ ["And she's hooked to the silver screen"],
+ {type: 'text/plain'})
+ },
+ "att2.txt": {
+ "content_type": "text/plain",
+ "data": new Blob(
+ ["But the film is a saddening bore"],
+ {type: 'text/plain'})
+ },
+ "att3.txt": {
+ "content_type": "text/plain",
+ "data": new Blob(
+ ["For she's lived it ten times or more"],
+ {type: 'text/plain'})
+ }
+ }
+};
+db.put(doc).then(function (result) {
+ // handle result
+}).catch(function (err) {
+ console.log(err);
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+See [Inline Attachments](https://docs.couchdb.org/en/stable/api/document/common.html?highlight=inline%20attachment#creating-multiple-attachments)
+on the CouchDB wiki for details.
+
+
diff --git a/docs/version/9.0.0/api/sync.html b/docs/version/9.0.0/api/sync.html
new file mode 100644
index 0000000000..eb75ed15b7
--- /dev/null
+++ b/docs/version/9.0.0/api/sync.html
@@ -0,0 +1,96 @@
+{% include anchor.html edit="true" title="Sync a database" hash="sync" %}
+
+{% highlight js %}
+const sync = PouchDB.sync(src, target, [options])
+{% endhighlight %}
+
+Sync data from `src` to `target` and `target` to `src`. This is a convenience method for bidirectional data replication.
+
+In other words, this code:
+
+{% highlight js %}
+PouchDB.replicate('mydb', 'http://localhost:5984/mydb');
+PouchDB.replicate('http://localhost:5984/mydb', 'mydb');
+{% endhighlight %}
+
+
+is equivalent to this code:
+
+{% highlight js %}
+PouchDB.sync('mydb', 'http://localhost:5984/mydb');
+{% endhighlight %}
+
+
+### Options
+
+* `options.push` + `options.pull`: Allows you to specify separate [replication options](api.html#replication) for the individual replications.
+
+Replication options such as `filter` passed to sync directly will be passed to both replications. Please refer to [replicate()](api.html#replication) for documentation on those options.
+
+#### Example Usage:
+{% highlight js %}
+const sync = PouchDB.sync('mydb', 'http://localhost:5984/mydb', {
+ live: true,
+ retry: true
+}).on('change', function (info) {
+ // handle change
+}).on('paused', function (err) {
+ // replication paused (e.g. replication up to date, user went offline)
+}).on('active', function () {
+ // replicate resumed (e.g. new changes replicating, user went back online)
+}).on('denied', function (err) {
+ // a document failed to replicate (e.g. due to permissions)
+}).on('complete', function (info) {
+ // handle complete
+}).on('error', function (err) {
+ // handle error
+});
+
+sync.cancel(); // whenever you want to cancel
+{% endhighlight %}
+
+There is also a shorthand for syncing given existing PouchDB objects. This behaves the same as `PouchDB.sync()`:
+
+{% highlight js %}
+db.sync(remoteDB, [options]);
+{% endhighlight %}
+
+It is also possible to combine "one-way" replication and sync for performance reasons.
+When your PouchDB application starts up it could perform a one-off, one-way replication to completion and then initiate the two-way, continuous retryable sync:
+
+{% highlight js %}
+const url = 'http://localhost:5984/mydb';
+const opts = { live: true, retry: true };
+
+// do one way, one-off sync from the server until completion
+db.replicate.from(url).on('complete', function(info) {
+ // then two-way, continuous, retriable sync
+ db.sync(url, opts)
+ .on('change', onSyncChange)
+ .on('paused', onSyncPaused)
+ .on('error', onSyncError);
+}).on('error', onSyncError);
+{% endhighlight %}
+
+The above technique results in fewer HTTP requests being used and better performance than just using `db.sync` on its own.
+
+#### Example Response:
+
+Change events in `sync` have an extra property `direction` which refers to the direction the change was going. Its value will either be `push` or `pull`.
+
+{% highlight js %}
+{ direction: 'push',
+ change:
+ { ok: true,
+ start_time: '2015-10-21T15:26:51.151Z',
+ docs_read: 1,
+ docs_written: 1,
+ doc_write_failures: 0,
+ errors: [],
+ last_seq: 1,
+ docs: [ [Object] ] } }
+{% endhighlight %}
+
+For any further details, please refer to [replicate()](api.html#replication).
+
+
diff --git a/docs/version/9.0.0/api/view_cleanup.html b/docs/version/9.0.0/api/view_cleanup.html
new file mode 100644
index 0000000000..4df285312d
--- /dev/null
+++ b/docs/version/9.0.0/api/view_cleanup.html
@@ -0,0 +1,47 @@
+{% include anchor.html edit="true" title="View cleanup" hash="view_cleanup" %}
+
+{% highlight js %}
+db.viewCleanup([callback])
+{% endhighlight %}
+
+Cleans up any stale map/reduce indexes.
+
+As design docs are deleted or modified, their associated index files (in CouchDB) or companion databases (in local PouchDBs) continue to take up space on disk. `viewCleanup()` removes these unnecessary index files.
+
+See [the CouchDB documentation on view cleanup](http://couchdb.readthedocs.org/en/latest/maintenance/compaction.html#views-cleanup) for details.
+
+#### Example Usage:
+
+{% include code/start.html id="viewcleanup" type="callback" %}
+{% highlight js %}
+db.viewCleanup(function (err, result) {
+ if (err) { return console.log(err); }
+ // handle result
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="viewcleanup" type="async" %}
+{% highlight js %}
+try {
+ const result = await db.viewCleanup();
+} catch (err) {
+ console.log(err);
+}
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="viewcleanup" type="promise" %}
+{% highlight js %}
+db.viewCleanup().then(function (result) {
+ // handle result
+}).catch(function (err) {
+ console.log(err);
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+#### Example Response:
+{% highlight js %}
+{ "ok" : "true" }
+{% endhighlight %}
\ No newline at end of file
diff --git a/docs/version/9.0.0/custom.md b/docs/version/9.0.0/custom.md
new file mode 100644
index 0000000000..b8095cc4aa
--- /dev/null
+++ b/docs/version/9.0.0/custom.md
@@ -0,0 +1,539 @@
+---
+layout: 2ColLeft.html
+title: Custom Builds
+sidebar: ./nav.html
+---
+
+PouchDB supports custom builds, meaning you can pick and choose the features of
+PouchDB that you want to use, potentially resulting in smaller bundle sizes
+and faster build times.
+
+PouchDB exposes its custom builds via separate packages available on npm. All of
+these packages follow the format `pouchdb-` and can be installed using `npm install`.
+
+Some packages are included by default in the main `pouchdb` package, whereas
+others (including third-party packages) must be installed separately.
+
+{% include alert/start.html variant="warning"%}
+{% markdown %}
+
+Custom builds require an [npm][]-based build system, using a bundler
+like [Browserify][], [Webpack][], [SystemJS][], [Rollup][], or [JSPM][]. Tools like
+[Bower][], as well as direct download of prebuilt JavaScript files, are not supported.
+
+[Browserify]: http://browserify.org/
+[Webpack]: http://webpack.github.io/
+[SystemJS]: https://github.com/systemjs/systemjs
+[JSPM]: http://jspm.io/
+[Rollup]: http://rollupjs.org/
+[npm]: http://npmjs.com/
+[Bower]: http://bower.io
+
+{% endmarkdown %}
+{% include alert/end.html%}
+
+PouchDB packages come in three flavors: *presets*, *plugins*, and
+*utilities*.
+
+**Presets** are a collection of plugins, which expose a `PouchDB` object that is ready to be used.
+
+**Plugins** are features that can be added to a `PouchDB` instance using the `PouchDB.plugin()`
+API.
+
+**Utilities** are grab-bags of helper functions, and are only recommended for advanced use cases.
+
+#### Quick links
+
+* [Presets](#presets)
+* [Plugins](#plugins)
+* [Utilities](#utilities)
+
+{% include anchor.html class="h2" title="Presets" hash="presets" %}
+
+Presets export a `PouchDB` object and contain a built-in set of PouchDB
+plugins. You are free to create your own presets, but PouchDB provides a few first-party presets to address common use cases.
+
+### [pouchdb-browser](https://npmjs.org/package/pouchdb-browser)
+
+The `pouchdb-browser` preset contains the version of PouchDB that is designed
+for the browser. In particular, it ships with the IndexedDB adapter
+as its default adapter. It also contains the replication, HTTP, and map/reduce plugins.
+
+Use this preset if you only want to use PouchDB in the browser,
+and don't want to use it in Node.js. (E.g. to avoid installing LevelDB.)
+
+#### Example Usage
+
+```bash
+npm install pouchdb-browser
+```
+
+```js
+const PouchDB = require('pouchdb-browser');
+const db = new PouchDB('mydb');
+```
+
+#### Source code (simplified)
+
+```js
+const PouchDB = require('pouchdb-core')
+ .plugin(require('pouchdb-adapter-idb'))
+ .plugin(require('pouchdb-adapter-http'))
+ .plugin(require('pouchdb-mapreduce'))
+ .plugin(require('pouchdb-replication'));
+```
+
+### [pouchdb-node](https://npmjs.org/package/pouchdb-node)
+
+The `pouchdb-node` preset contains the version of PouchDB that is designed for
+Node.js. In particular, it uses the LevelDB adapter and doesn't ship with the
+IndexedDB or WebSQL adapters. It also contains the replication, HTTP, and map/reduce plugins.
+
+Use this preset if you are only using PouchDB in Node, and not in the browser.
+
+#### Example Usage
+
+```bash
+npm install pouchdb-node
+```
+
+```js
+const PouchDB = require('pouchdb-node');
+const db = new PouchDB('mydb');
+```
+
+#### Source code (simplified)
+
+```js
+const PouchDB = require('pouchdb-core')
+ .plugin(require('pouchdb-adapter-leveldb'))
+ .plugin(require('pouchdb-adapter-http'))
+ .plugin(require('pouchdb-mapreduce'))
+ .plugin(require('pouchdb-replication'));
+```
+
+### [pouchdb-core](https://npmjs.org/package/pouchdb-core)
+
+The `pouchdb-core` package is a special preset in that it exposes the minimum
+number of APIs. It contains zero plugins and is designed to be used in addition
+with other plugins. By itself, it probably isn't very useful.
+
+#### Example Usage
+
+```bash
+npm install pouchdb-core
+```
+
+```js
+const PouchDB = require('pouchdb-core');
+PouchDB.plugin(/* attach plugins to make me more interesting! */);
+```
+
+{% include anchor.html class="h2" title="Plugins" hash="plugins" %}
+
+Plugins contain functionality that can be added to a `PouchDB` instance using `PouchDB.plugin()`. There are many [third-party plugins](/external.html), but the ones described below are first-party plugins, which are given the same level of support as PouchDB itself. Some first-party plugins are included in the default `pouchdb` build, whereas others aren't.
+
+There is also a special type of plugin called an _adapter plugin_. Adapter plugins (such as IndexedDB, WebSQL, LevelDB, and HTTP) determine the storage format that
+PouchDB uses. For the non-HTTP adapters, the plugin order matters, i.e. if you
+want IndexedDB to be preferred to WebSQL, then you should load it first.
+
+### [pouchdb-adapter-idb](https://npmjs.org/package/pouchdb-adapter-idb)
+
+The primary adapter used by PouchDB in the browser, using IndexedDB. The adapter
+name is `'idb'`.
+
+#### Example usage
+
+```bash
+npm install pouchdb-adapter-idb
+```
+
+```js
+PouchDB.plugin(require('pouchdb-adapter-idb'));
+const db = new PouchDB('mydb', {adapter: 'idb'});
+console.log(db.adapter); // 'idb'
+```
+
+### [pouchdb-adapter-websql](https://npmjs.org/package/pouchdb-adapter-websql)
+
+An adapter used by PouchDB in the browser, using WebSQL. The adapter
+name is `'websql'`.
+
+Before PouchDB 7.0.0, this was shipped as a default adapter. As of PouchDB 7.0.0, it must be loaded as a separate plugin.
+
+#### Example usage
+
+```bash
+npm install pouchdb-adapter-websql
+```
+
+```js
+PouchDB.plugin(require('pouchdb-adapter-websql'));
+const db = new PouchDB('mydb', {adapter: 'websql'});
+console.log(db.adapter); // 'websql'
+```
+
+### [pouchdb-adapter-leveldb](https://npmjs.org/package/pouchdb-adapter-leveldb)
+
+{% include alert/start.html variant="warning"%}
+{% markdown %}
+**Warning: deprecation notice.** The `leveldb` adapter will be deprecated in PouchDB version 10.0.0 and removed in version 11.0.0. You can read [the migration guide here](https://pouchdb.com/2026/04/10/migration-to-nodesqlite.html) and more about the topic in [this link](https://github.com/apache/pouchdb/issues/9163).
+{% endmarkdown %}
+{% include alert/end.html%}
+
+The primary adapter used by PouchDB in Node.js, using LevelDB. The adapter name
+is `'leveldb'`.
+
+#### Example usage
+
+```bash
+npm install pouchdb-adapter-leveldb
+```
+
+```js
+PouchDB.plugin(require('pouchdb-adapter-leveldb'));
+const db = new PouchDB('mydb', {adapter: 'leveldb'});
+console.log(db.adapter); // 'leveldb'
+```
+
+### [pouchdb-adapter-http](https://npmjs.org/package/pouchdb-adapter-http)
+
+The primary adapter used by PouchDB in both Node.js and the browser for communicating
+with external CouchDB (or CouchDB-like) servers.
+
+This plugin can be added to PouchDB in any order, and is somewhat special in that
+you must pass in a name like `'http://...'` in order to use it. The adapter name
+is either `'http'` or `'https'` depending on the protocol.
+
+#### Example usage
+
+```bash
+npm install pouchdb-adapter-http
+```
+
+```js
+PouchDB.plugin(require('pouchdb-adapter-http'));
+const db = new PouchDB('http://127.0.0.1:5984/mydb');
+console.log(db.adapter); // 'http'
+```
+
+### [pouchdb-adapter-memory](https://npmjs.org/package/pouchdb-adapter-memory)
+
+{% include alert/start.html variant="warning"%}
+{% markdown %}
+**Warning: deprecation notice.** The `memory` adapter will be deprecated in PouchDB version 10.0.0 and removed in version 11.0.0. You can read [the migration guide here](https://pouchdb.com/2026/04/10/migration-to-nodesqlite.html) and more about the topic in [this link](https://github.com/apache/pouchdb/issues/9163).
+{% endmarkdown %}
+{% include alert/end.html%}
+
+An optional adapter that works in the browser and Node.js, fully in-memory. The adapter name
+is `'memory'`.
+
+#### Example usage
+
+```bash
+npm install pouchdb-adapter-memory
+```
+
+```js
+PouchDB.plugin(require('pouchdb-adapter-memory'));
+const db = new PouchDB('mydb', {adapter: 'memory'});
+console.log(db.adapter); // 'memory'
+```
+
+### [pouchdb-adapter-localstorage](https://npmjs.org/package/pouchdb-adapter-localstorage)
+
+An optional adapter that works in the browser using LocalStorage. The adapter name
+is `'localstorage'`.
+
+#### Example usage
+
+```bash
+npm install pouchdb-adapter-localstorage
+```
+
+```js
+PouchDB.plugin(require('pouchdb-adapter-localstorage'));
+const db = new PouchDB('mydb', {adapter: 'localstorage'});
+console.log(db.adapter); // 'localstorage'
+```
+
+### [pouchdb-adapter-node-websql](https://npmjs.org/package/pouchdb-adapter-node-websql)
+
+An optional adapter that works in Node.js using SQLite via [node-websql](https://github.com/nolanlawson/node-websql). The adapter name
+is `'websql'`.
+
+#### Example usage
+
+```bash
+npm install pouchdb-adapter-node-websql
+```
+
+```js
+PouchDB.plugin(require('pouchdb-adapter-node-websql'));
+const db = new PouchDB('mydb', {adapter: 'websql'});
+console.log(db.adapter); // 'websql'
+```
+
+### [pouchdb-adapter-indexeddb](https://npmjs.org/package/pouchdb-adapter-indexeddb)
+
+{% include alert/start.html variant="warning"%}
+{% markdown %}
+
+**Warning: experimental.** You probably don't want to use this yet. 😉
+
+{% endmarkdown %}
+{% include alert/end.html%}
+
+An experimental next-generation IndexedDB adapter, also known as "idb-next". Currently not shipped as part of PouchDB. The adapter name is `'indexeddb'`.
+
+#### Example usage
+
+```bash
+npm install pouchdb-adapter-indexeddb
+```
+
+```js
+PouchDB.plugin(require('pouchdb-adapter-indexeddb'));
+const db = new PouchDB('mydb', {adapter: 'indexeddb'});
+console.log(db.adapter); // 'indexeddb'
+```
+
+### [pouchdb-find](https://npmjs.org/package/pouchdb-find)
+
+PouchDB's "Mango" query API, exposed via the `find()`, `listIndexes(), `createIndex()`, and `deleteIndex()` methods. Not shipped by default in PouchDB.
+
+#### Example usage
+
+```bash
+npm install pouchdb-find
+```
+
+```js
+PouchDB.plugin(require('pouchdb-find'));
+const db = new PouchDB('mydb');
+db.find(/* see API docs for full info */);
+```
+
+### [pouchdb-mapreduce](https://npmjs.org/package/pouchdb-mapreduce)
+
+PouchDB's map/reduce API, exposed via the `query()` and `viewCleanup()` methods. Ships by default in PouchDB.
+
+#### Example usage
+
+```bash
+npm install pouchdb-mapreduce
+```
+
+```js
+PouchDB.plugin(require('pouchdb-mapreduce'));
+const db = new PouchDB('mydb');
+db.query(/* see query API docs for full info */);
+```
+
+### [pouchdb-replication](https://npmjs.org/package/pouchdb-replication)
+
+PouchDB's replication API, exposed via the `replicate()` and `sync()` methods. Ships by default in PouchDB.
+
+#### Example usage
+
+```bash
+npm install pouchdb-replication
+```
+
+```js
+PouchDB.plugin(require('pouchdb-replication'));
+const db = new PouchDB('mydb');
+db.replicate(/* see replicate/sync API docs for full info */);
+```
+
+{% include anchor.html class="h2" title="Utilities" hash="utilities" %}
+
+These utilities are intended only for advanced users of PouchDB, such as
+third-party plugin authors. Formerly, many of them were exposed via the `extras/` API, which
+is now deprecated.
+
+Most of these are internal, and the APIs are not thoroughly documented. You will
+most likely need to read the source code to understand how they work.
+
+{% include alert/start.html variant="warning"%}
+{% markdown %}
+
+**Warning:** you are entering a semver-free zone.
+
+In contrast to the presets and plugins listed above, **none of the following packages
+follow semver**. Their versions are pinned to PouchDB's, and may change at any time
+without warning. You are strongly recommended to **use exact versions** when installing these packages.
+
+{% endmarkdown %}
+{% include alert/end.html%}
+
+### [pouchdb-abstract-mapreduce](https://npmjs.org/package/pouchdb-abstract-mapreduce)
+
+The underlying logic for secondary indexes, as expressed in both `pouchdb-mapreduce` and `pouchdb-find`. Both packages use this package under the hood.
+
+#### Example usage
+
+```bash
+npm install --save-exact pouchdb-abstract-mapreduce
+```
+
+### [pouchdb-adapter-utils](https://npmjs.org/package/pouchdb-adapter-utils)
+
+Utilities for PouchDB adapters.
+
+#### Example usage
+
+```bash
+npm install --save-exact pouchdb-adapter-utils
+```
+
+### [pouchdb-ajax](https://npmjs.org/package/pouchdb-ajax)
+
+PouchDB's `ajax()` function.
+
+#### Example usage
+
+```bash
+npm install --save-exact pouchdb-ajax
+```
+
+### [pouchdb-binary-utils](https://npmjs.org/package/pouchdb-binary-utils)
+
+Utilities for operating on binary strings and Buffers/Blobs.
+
+#### Example usage
+
+```bash
+npm install --save-exact pouchdb-binary-utils
+```
+
+### [pouchdb-checkpointer](https://npmjs.org/package/pouchdb-checkpointer)
+
+Tool to write a checkpoint, e.g. during replication.
+
+#### Example usage
+
+```bash
+npm install --save-exact pouchdb-checkpointer
+```
+
+### [pouchdb-collate](https://npmjs.org/package/pouchdb-collate)
+
+Collation functions for PouchDB map/reduce.
+
+#### Example usage
+
+```bash
+npm install --save-exact pouchdb-collate
+```
+
+### [pouchdb-collections](https://npmjs.org/package/pouchdb-collections)
+
+ES6 shims for Map and Set as used in PouchDB.
+
+#### Example usage
+
+```bash
+npm install --save-exact pouchdb-collections
+```
+
+### [pouchdb-errors](https://npmjs.org/package/pouchdb-errors)
+
+Errors exposed by PouchDB.
+
+#### Example usage
+
+```bash
+npm install --save-exact pouchdb-errors
+```
+
+### [pouchdb-generate-replication-id](https://npmjs.org/package/pouchdb-generate-replication-id)
+
+Function to generate a replication ID to mark progress during replications.
+
+#### Example usage
+
+```bash
+npm install --save-exact pouchdb-generate-replication-id
+```
+
+### [pouchdb-json](https://npmjs.org/package/pouchdb-json)
+
+Utilities for safely stringifying and parsing JSON.
+
+#### Example usage
+
+```bash
+npm install --save-exact pouchdb-json
+```
+
+### [pouchdb-mapreduce-utils](https://npmjs.org/package/pouchdb-mapreduce-utils)
+
+Utilities used by `pouchdb-mapreduce`.
+
+#### Example usage
+
+```bash
+npm install --save-exact pouchdb-mapreduce-utils
+```
+
+### [pouchdb-md5](https://npmjs.org/package/pouchdb-md5)
+
+Utilities for calculating MD5 checksums.
+
+#### Example usage
+
+```bash
+npm install --save-exact pouchdb-md5
+```
+
+### [pouchdb-merge](https://npmjs.org/package/pouchdb-merge)
+
+PouchDB's CouchDB-style document merge algorithm.
+
+#### Example usage
+
+```bash
+npm install --save-exact pouchdb-merge
+```
+
+### [pouchdb-promise](https://npmjs.org/package/pouchdb-promise)
+
+A `Promise` object, polyfilled using `lie` if Promises aren't available globally.
+
+#### Example usage
+
+```bash
+npm install --save-exact pouchdb-promise
+```
+
+### [pouchdb-selector-core](https://npmjs.org/package/pouchdb-selector-core)
+
+The core Mango selector logic, which allows the selector to be used both by `pouchdb-find` and for filtering/replication.
+
+#### Example usage
+
+```bash
+npm install --save-exact pouchdb-selector-core
+```
+
+### [pouchdb-utils](https://npmjs.org/package/pouchdb-utils)
+
+A potpourri of miscellaneous utilities used by PouchDB and its sub-packages.
+
+#### Example usage
+
+```bash
+npm install --save-exact pouchdb-utils
+```
+
+### [sublevel-pouchdb](https://npmjs.org/package/sublevel-pouchdb)
+
+Fork of [level-sublevel](https://github.com/dominictarr/level-sublevel)
+with only the subset of the API that PouchDB uses.
+
+#### Example usage
+
+```bash
+npm install --save-exact sublevel-pouchdb
+```
diff --git a/docs/version/9.0.0/download.md b/docs/version/9.0.0/download.md
new file mode 100644
index 0000000000..a461e55bc3
--- /dev/null
+++ b/docs/version/9.0.0/download.md
@@ -0,0 +1,75 @@
+---
+layout: 2ColLeft.html
+title: Download
+sidebar: ./nav.html
+---
+
+{% include anchor.html class="h3" title="Quick Start" hash="file" %}
+
+{% highlight html %}
+
+
+{% endhighlight %}
+
+PouchDB can also be directly downloaded:
+
+* [pouchdb-{{ site.version }}.min.js][latest-min] (compressed for production)
+* [pouchdb-{{ site.version }}.js][latest] (uncompressed for debugging)
+
+If you are using PouchDB in Internet Explorer a [Promise](https://www.npmjs.com/package/promise-polyfill) and [Fetch](https://www.npmjs.com/package/whatwg-fetch) polyfill will be needed.
+
+{% include anchor.html class="h3" title="npm" hash="npm" %}
+
+PouchDB can be installed through [npm](https://npmjs.com):
+
+{% highlight bash %}npm install --save pouchdb{% endhighlight %}
+
+After installing, call `require()` to use it:
+
+{% highlight javascript %}
+const PouchDB = require('pouchdb');
+const db = new PouchDB('my_database');
+{% endhighlight %}
+
+PouchDB can be used either in Node or in the browser. A bundler such as [Browserify](https://browserify.org/), [Webpack](https://webpack.github.io/), or [Rollup](https://rollupjs.org/) is needed for browser usage.
+
+#### Browser only
+
+If you're only using PouchDB in the browser, you can use `pouchdb-browser` for
+faster install times:
+
+{% highlight bash %}npm install --save pouchdb-browser{% endhighlight %}
+
+{% highlight javascript %}
+const PouchDB = require('pouchdb-browser');
+const db = new PouchDB('my_database');
+{% endhighlight %}
+
+See [custom builds]({{ site.baseurl }}/custom.html) for more options.
+
+{% include anchor.html class="h3" title="CDNs" hash="cdn" %}
+
+PouchDB is hosted at these CDNs:
+
+* [cdnjs](https://cdnjs.com/libraries/pouchdb)
+* [jsdelivr](https://www.jsdelivr.com/#!pouchdb)
+* [unpkg](https://unpkg.com/pouchdb@{{ site.version }}/dist/)
+
+{% highlight bash %}bower install --save pouchdb{% endhighlight %}
+
+{% include anchor.html class="h3" title="Past releases" hash="past-releases" %}
+
+For past releases and changelog, check out the [Github releases page](https://github.com/apache/pouchdb/releases).
+
+{% include anchor.html class="h3" title="Plugins" hash="plugins" %}
+
+For third-party plugins, see the [plugins page](/external.html).
+
+{% include anchor.html class="h3" title="Custom builds" hash="custom" %}
+
+For custom builds and first-party plugins, see the [custom builds]({{ site.baseurl }}/custom.html) page.
+
+[latest]: https://github.com/apache/pouchdb/releases/download/{{ site.version }}/pouchdb-{{ site.version }}.js
+[latest-min]: https://github.com/apache/pouchdb/releases/download/{{ site.version }}/pouchdb-{{ site.version }}.min.js
diff --git a/docs/version/9.0.0/errors.md b/docs/version/9.0.0/errors.md
new file mode 100644
index 0000000000..9fa2b7ba8e
--- /dev/null
+++ b/docs/version/9.0.0/errors.md
@@ -0,0 +1,321 @@
+---
+layout: 2ColLeft.html
+title: Common Errors
+sidebar: ./nav.html
+---
+
+{% include anchor.html class="h3" title="No `Access-Control-Allow-Origin` header" hash="no_access_control_allow_origin_header" %}
+
+If you see the error:
+
+```bash
+XMLHttpRequest cannot load [...]
+No 'Access-Control-Allow-Origin' header is present on the requested resource.
+Origin [...] is therefore not allowed access.
+```
+
+or this one:
+
+```bash
+Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at http://[couchDBIP]:[couchDBPort]/[dbname]/?_nonce=[request hash]. This can be fixed by moving the resource to the same domain or enabling CORS
+```
+
+it's because you need to enable [CORS](https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS) on CouchDB/Cloudant/whatever you're using. Otherwise, your scripts can only access the server database if they're served from the same origin — the protocol (ex: _http://_, _https://_), domain, and port number must match.
+
+You can enable CORS in CouchDB using `curl` or the Futon web interface, but we've saved you some time by making a Node script called [add-cors-to-couchdb](https://github.com/pouchdb/add-cors-to-couchdb). Just run:
+
+{% highlight bash %}
+$ npm install -g add-cors-to-couchdb
+$ add-cors-to-couchdb
+{% endhighlight %}
+
+Or if your database is not at `127.0.0.1:5984`:
+
+{% highlight bash %}
+$ add-cors-to-couchdb http://me.example.com \
+ -u myusername -p mypassword
+{% endhighlight %}
+
+You can check that CORS is now enabled by visiting [http://localhost:5984/_utils/config.html](http://localhost:5984/_utils/config.html) in your browser. You should see something like this:
+
+{% include img.html src="cors_in_couchdb.png" alt="CORS settings in CouchDB" %}
+
+{% include anchor.html class="h3" title="PouchDB throws a `No valid adapter found` error" hash="no_valid_adapter" %}
+
+Reading from/writing to a local database from an `iframe` with a different origin will cause PouchDB to throw a `No valid adapter found` error in Firefox. This is due to Firefox's IndexedDB implementation.
+
+IndexedDB has a same-origin restriction. Read/write operations from another origin will always fail, but only Firefox triggers a `No valid adapter found` error. Chrome / Opera will instead throw an [`UnknownError`](#unknown_error_chrome).
+
+{% include anchor.html class="h3" title="iOS/Safari: \"there was not enough remaining storage space\"" hash="not_enough_space" %}
+
+On iOS and Safari, if you expect your app to use more than 5MB of space, you will need to request the space up-front from the user. In certain versions of Safari, you can never request more than what the user originally grants you.
+
+{% include img.html src="safari_popup.png" alt="Safari storage quota popup" %}
+
+To get around this, when you create your PouchDB, use the `opts.size` option for the expected _maximum_ size of the database in MB. Valid increments are 10, 50, 100, 500, and 1000. For instance, if you request 50, then Safari will show a popup saying "allow 50MB?" but if you request 51, then Safari will show a popup saying "allow 100MB?".
+
+If you don't use the `size` option, then you'll be able to use up to 5MB without any popup, but then once you use more, there will be a popup asking for 10.
+
+```js
+new PouchDB('mydb', {size: 10}); // request 10 MB with a popup
+new PouchDB('mydb', {size: 50}); // request 50 MB with a popup
+new PouchDB('mydb'); // implicitly request 5 MB, no popup until you exceed 5MB
+```
+
+This does not affect any backend other than Web SQL. Chrome, Android, and Opera do not show the popup. On PhoneGap/Cordova apps, you can also use the [SQLite plugin][sqlite] to get around this problem. Here's [more information about storage quotas](http://www.html5rocks.com/en/tutorials/offline/quota-research) and [details on the Safari/iOS bug](https://github.com/apache/pouchdb/issues/2347).
+
+{% include anchor.html class="h3" title="PouchDB throws 404 (Object Not Found) for '_local' document" hash="404__local_document" %}
+
+Don't worry, nothing is amiss, this is expected behaviour:
+During PouchDB's initial replication PouchDB will check for a checkpoint, if it doesn't exist a 404 will be returned and a checkpoint will subsequently be written.
+
+{% include anchor.html class="h3" title="PouchDB is throwing `InvalidStateError`" hash="throwing_invalidstateerror" %}
+
+Are you in private browsing mode? IndexedDB is [disabled in private browsing mode](https://developer.mozilla.org/en-US/docs/IndexedDB/Using_IndexedDB) in Firefox.
+
+{% include anchor.html class="h3" title="Can't open second database on Android WebView with Cordova/Phone Gap" hash="phonegap_cordova_second_database" %}
+
+There is a limit of one database per app in some versions of the Android WebView. Install the [SQLite plugin][sqlite], then PouchDB will use that if it is available.
+
+{% include anchor.html class="h3" title="Possible EventEmitter memory leak detected" hash="event_emitter_limit" %}
+
+If you see this warning:
+
+```bash
+(node) warning: possible EventEmitter memory leak detected. 11 listeners added.
+Use emitter.setMaxListeners() to increase limit.
+```
+
+This is because PouchDB uses Node-style [EventEmitters](https://nodejs.org/api/events.html) for its events. An EventEmitter is any object that has an `.on()` or `once()` method, such as `db.changes().on('change', ...`.
+
+By default, all EventEmitters have 10 listeners, and if you exceed that limit, e.g. by attaching many `changes()` listeners or running many simultaneous `replicate()` or `sync()` events, then you may exceed this limit.
+
+**This could indicate a memory leak in your code**. Check to make sure that you are calling `cancel()` on any `changes()`, `replicate()`, or `sync()` handlers, if you are constantly starting and stopping those events.
+
+If you're sure it's not a memory leak, though, you can increase the limit by doing:
+
+{% highlight javascript %}
+db.setMaxListeners(20); // or 30 or 40 or however many you need
+{% endhighlight %}
+
+In the above example, `db` refers to a database object you created using `new PouchDB('dbname')`.
+
+{% include anchor.html class="h3" title="Database size limitation of ~5MB on iOS with Cordova/Phone Gap" hash="size_limitation_5mb" %}
+
+If you're storing large amounts of data, such as PNG attachments, the [SQLite plugin][sqlite] is again your friend. (See [issue #1260](https://github.com/apache/pouchdb/issues/1260) for details.)
+
+{% include anchor.html class="h3" title="CouchDB returns a 404 for GETs from a CouchApp" hash="404_get_couchapp" %}
+
+Certain URL rewrites are broken by PouchDB's cache-busting; try adding `{cache : false}` to the PouchDB constructor. (See [issue #1233](https://github.com/apache/pouchdb/issues/1233) for details.)
+
+{% include anchor.html class="h3" title="Uncaught TypeError: object is not a function" hash="typeerror_object_is_not_a_function" %}
+
+Did you include the [es6-promise shim library](https://github.com/jakearchibald/es6-promise)? Not every browser implements ES6 Promises correctly. (See [issue #1747](https://github.com/apache/pouchdb/issues/1747) for details.)
+
+{% include anchor.html class="h3" title="SyntaxError: Parse error (Cordova on Android)" hash="cordova_android_parse_error" %}
+
+Did you include the [es5-shim library][es5shim]? PouchDB is written in ES5, which is supported by modern browsers, but requires shims for older browsers (e.g. IE 9, Android 2.1 WebView).
+
+In Android, if you're loading PouchDB directly via `webView.loadUrl('javascript://' + js')`, you should prefer the minified PouchDB javascript file to the unminified one, since code comments can also cause parse errors.
+
+{% include anchor.html class="h3" title="PouchDB object fails silently (Safari)" hash="safari_object_silent_fail" %}
+
+Safari requires users to confirm that they want to allow an app to store data locally ("Allow this website to use space on your disk?"). If PouchDB is loaded in an `iframe` or some other unusual way, the dialog might not be shown, and the database will silently fail.
+
+{% include anchor.html class="h3" title="window.localStorage is not available (Chrome apps)" hash="window_localstorage_chrome_apps" %}
+
+In Chrome apps, you'll see the warning "window.localStorage is not available in packaged apps. Use chrome.storage.local instead." This is harmless; since PouchDB doesn't use localStorage if it's not available.
+
+{% include anchor.html class="h3" title="Error: UnknownError (Firefox)" hash="unknown_error_ff" %}
+
+Are you using a webserver to host and run your code? This error can be caused by running your script/file locally using the `file:///` setting in Firefox, since Firefox does not [allow access to IndexedDB locally](https://bugzilla.mozilla.org/show_bug.cgi?id=643318). You can use the SimpleHTTPServer to deploy your script by running `python -m SimpleHTTPServer` from the directory containing the script, or use the Apache webserver and then access the script by using `http://localhost/{path_to_your_script}`.
+
+{% include anchor.html class="h3" title="Error: UnknownError (Chrome / Opera)" hash="unknown_error_chrome" %}
+
+This can occur when attempting to read from or write to IndexedDB from a different origin. IndexedDB has a same-origin restriction. Attempting to write to the database associated with _http://example.com_ from an `iframe` served from _http://api.example.com_, for example, will fail.
+
+In Firefox, PouchDB instead throws a [`No valid adapter found`](#no_valid_adapter) error.
+
+{% include anchor.html class="h3" title="DataCloneError: An object could not be cloned" hash="could_not_be_cloned" %}
+
+If you ever see:
+
+```bash
+Uncaught DataCloneError:
+ Failed to execute 'put' on 'IDBObjectStore':
+ An object could not be cloned.
+```
+
+Or:
+
+```bash
+DataCloneError: The object could not be cloned.
+```
+
+Then the problem is that the document you are trying to store is not a pure JSON object. For example, an object with its own class (`new Foo()`) or with special methods like getters and setters cannot be stored in PouchDB/CouchDB.
+
+If you are ever unsure, then run this on the document:
+
+```js
+JSON.parse(JSON.stringify(myDocument));
+```
+
+If the object you get out is the same as the object you put in, then you are storing the right kind of object.
+
+Note that this also means that you cannot store `Date`s in your document. You must convert them to strings or numbers first. `Date`s will be stored as-is in IndexedDB, but in the other adapters and in CouchDB, they will be automatically converted to ISO string format, e.g. `'2015-01-01T12:00:00.000Z'`. This can caused unwanted results. See [#2351](https://github.com/apache/pouchdb/issues/2351) and [#2158](https://github.com/apache/pouchdb/issues/2158) for details.
+
+{% include anchor.html class="h3" title="DOM Exception 18 in Android pre-Kitkat WebView" hash="android_pre_kitkat" %}
+
+This applies to hybrid apps designed to run in Android pre-Kitkat (i.e. before 4.4).
+
+If you are directly using a `WebView` and not using Cordova/PhoneGap, you will probably either run into an error where PouchDB silently fails or you see `Error: SECURITY_ERR: DOM Exception 18` in the console. As a sanity test, you can run this JavaScript:
+
+```js
+openDatabase('mydatabase', 1, 'mydatabase', 5000000, function (db) { console.log('it works!'); });
+```
+
+If you see "it works" in the console, then everything's peachy. Otherwise there are a few things you have to do.
+
+First, make sure Web SQL is enabled on your `WebView` in the first place using [setDatabaseEnabled](http://developer.android.com/reference/android/webkit/WebSettings.html#setDatabaseEnabled%28boolean%29):
+
+```java
+myWebView.getSettings().setDatabaseEnabled(true);
+```
+
+Second, specify a path for the database. Yes, you need to do this, even though it's deprecated in Kitkat:
+
+```java
+String databasePath = getContext().getApplicationContext().getDir(
+ "database", Context.MODE_PRIVATE).getPath();
+webView.getSettings().setDatabasePath(databasePath);
+```
+
+Third, you'll need to set an `onExceededDatabaseQuota` handler. Yes, it's also deprecated in Kitkat. Yes, you still need to do it.
+
+```java
+webView.setWebChromeClient(new WebChromeClient() {
+
+ @Override
+ public void onExceededDatabaseQuota(String url, String databaseIdentifier, long currentQuota, long estimatedSize,
+ long totalUsedQuota, WebStorage.QuotaUpdater quotaUpdater) {
+ quotaUpdater.updateQuota(estimatedSize * 2);
+ }
+});
+```
+
+If you skip any one of these three steps, then you will get the `DOM Exception 18` error. You need to do all three.
+
+Alternatively, you can also load the `WebView` with a fake `http://` URL, but this may cause other errors when you try to fetch files based on a relative path:
+
+```java
+webView.loadDataWithBaseURL("http://www.example.com",
+ htmlContent,
+ "text/html",
+ "utf-8",
+ null);
+```
+
+{% include anchor.html class="h3" title="Replication with attachments is slow or fails" hash="replicating_attachments_slow" %}
+
+The symptoms for this issues are:
+
+1. Replicating a database that has many attachments from a CouchDB server is either slow or fails randomly.
+2. You get server error message of the nature `No buffer space available`.
+
+Chances are that your server runs inside a virtual machine. The host system, or hypervisor, imposes limits on how much data each virtual machine can use for networking. If you are on a cheap virtual server, it is possible, that the default settings for PouchDB pull-replication (10 parallel batches of 100 documents each) exhaust the narrow limit of your server. Even a single client can cause this.
+
+The solution is to move to a better server, but if that is not an immediate option, a workaround would be reducing the `options.batch_size` and `options.batches_limit` [replication options]({{ site.baseurl }}/api.html#replication).
+
+To find optimal values, start by setting them both to 1 (meaning that PouchDB should download one document after the other) and increase from there and stop when the symptoms begin again. Note that multiple concurrent clients can still cause an issue, if you get too many. If all your documents have one or more attachments (e.g. a photos database), setting both options to `1` is probably a good idea.
+
+{% include alert/start.html variant="info" %}
+
+Generally, reducing these options that replicating the database down will take more time. Please test various settings to see what works for you and your hardware.
+
+{% include alert/end.html %}
+
+{% include alert/start.html variant="info" %}
+
+This issue has been found on OpenVZ systems, but other Hypervisors might also be affected. See
+[http://blog.aplikacja.info/105_no_buffer_space_available_on_openvz_vps.html](http://blog.aplikacja.info/105_no_buffer_space_available_on_openvz_vps.html)
+on how to diagnose this issue.
+
+{% include alert/end.html %}
+
+[es5shim]: https://github.com/es-shims/es5-shim
+[sqlite]: https://github.com/brodysoft/Cordova-SQLitePlugin
+
+{% include anchor.html class="h3" title="Packaging PouchDB in an app with WebPack" hash="package_pouchdb_webpack" %}
+
+PouchDB may have various dependencies that may not play nicely with WebPack. Here are some issues you may run into and their resolutions:
+
+**You may need an appropriate loader to handle this file type.**
+
+If you run into the following error (or similar):
+
+```bash
+ERROR in ./~/pouchdb/~/levelup/package.json
+Module parse failed: /path/to/node_modules/pouchdb/node_modules/levelup/package.json Line 2: Unexpected token :
+You may need an appropriate loader to handle this file type.
+| {
+| "name": "levelup",
+| "description": "Fast & simple storage - a Node.js-style LevelDB wrapper",
+| "version": "0.18.6",
+ @ ./~/pouchdb/~/levelup/lib/util.js 102:30-56
+
+```
+WebPack needs to be configured to recognize how to load json files. Simply, install `json-loader` and edit `webpack.config.js` as follows:
+
+```js
+module: {
+ loaders: [
+ // https://github.com/apache/pouchdb/issues/3319
+ {
+ test: /\.json$/,
+ loader: "json-loader"
+ }
+ ]
+}
+```
+
+{% include anchor.html class="h3" title="Failed to load resource: the server responded with a status of 400 (Bad request) " hash="couchbase_dbname" %}
+
+If you are using Couchbase Lite to sync with PouchDB then you cannot use capital letters in your database name as Couchbase Lite has restrictions on valid database names.
+
+{% include anchor.html class="h3" title="Live changes pass undetected (Web Extension)" hash="webextension_live_changes" %}
+
+When using PouchDB in a WebExtension (at least in Chromium 55 and Firefox 50), but apparently only if the `manifest.json` contains the `store` permission, a [live changes feed]({{ site.baseurl }}/guides/changes.html#live-changes-feed) in one tab or background process may not detect changes made in another tab/process. It will of course still report those changes upon the next change that it does detect.
+Minimal code to reproduce this can be found [here](https://gist.github.com/Treora/150dca4b57b1b881bd049303e82c5ced).
+
+{% include anchor.html class="h3" title="PouchDB install errors on Windows" hash="pouchdb_install_errors_windows" %}
+
+Sometimes `npm install pouchdb` fails on windows, when no prebuilt binary is available. The error looks similar to this:
+
+```bash
+C:\XXX\node_modules\project_name>if not defined npm_config_node_gyp (node "C:\XXX\node_modules\npm\bin\node-gyp-bin\....\node_modules\node-gyp\bin\node-gyp.js" rebuild ) else (node "" rebuild )
+gyp ERR! configure error
+gyp ERR! stack Error: Can't find Python executable "python", you can set the PYTHON env variable.
+gyp ERR! stack at PythonFinder.failNoPython (C:\XXX\nodejs\node_modules\npm\node_modules\node-gyp\lib\configure.js:483:19)
+```
+
+or something like this
+
+```bash
+gyp ERR! configure error
+npm ERR! code ELIFECYCLE
+npm ERR! errno 1
+npm ERR! leveldown@3.0.0 install: `prebuild-install || node-gyp rebuild`
+npm ERR! Exit status 1
+npm ERR!
+npm ERR! Failed at the leveldown@3.0.0 install script.
+```
+
+If you are on windows and getting any error messages mentioning **leveldown** and **Python**, please attempt the steps below.
+
+Node-Gyp requires Python 2.7, it does not work with Python 3.x unfortunately.
+
+Here are a few steps you can take:
+
+1. try `npm install --global --production windows-build-tools` (this command needs to be ran with admin privileges)
+2. read other [pointers to using node-gyp on Windows here](https://github.com/nodejs/node-gyp#on-windows)
+3. If you are only using PouchDB in the browser, you can install [pouchdb-browser](https://www.npmjs.com/package/pouchdb-browser) instead: `npm install --save pouchdb-browser`
diff --git a/docs/version/9.0.0/external.md b/docs/version/9.0.0/external.md
new file mode 100644
index 0000000000..95cbc41816
--- /dev/null
+++ b/docs/version/9.0.0/external.md
@@ -0,0 +1,306 @@
+---
+layout: 2ColLeft.html
+title: Plugins and External Projects
+sidebar: ./nav.html
+---
+
+Below is a list of known plugins, tools and projects can be used with PouchDB.
+
+## {% include anchor.html title="Plugins" hash="plugins" %}
+
+#### [PouchDB allDbs()](https://github.com/nolanlawson/pouchdb-all-dbs)
+
+Revives the `allDbs()` function, which lists all PouchDB databases.
+
+#### [PouchDB Authentication](https://github.com/nolanlawson/pouchdb-authentication)
+
+Plugin for CouchDB's authentication system.
+
+#### [Pouch Box](https://github.com/jo/pouch-box)
+
+Allows decentralized authentication and access control per document, using asymmetric encryption.
+
+#### [PouchDB Collate](https://github.com/apache/pouchdb/tree/master/packages/node_modules/pouchdb-collate)
+
+Collation functions for PouchDB map/reduce. Used by PouchDB map/reduce to maintain consistent [CouchDB collation ordering](https://wiki.apache.org/couchdb/View_collation).
+
+#### [Crypto Pouch](https://github.com/calvinmetcalf/crypto-pouch)
+
+Encrypt a PouchDB/CouchDB database.
+
+#### [Pouch Dat](https://github.com/calvinmetcalf/pouch-dat)
+
+Replicate from PouchDB to Dat.
+
+#### [PouchDB Hyperbee](https://github.com/RangerMauve/pouchdb-adapter-hyperbee)
+
+Sparsely load data from Hyperbee over p2p networks in Node and the Browser.
+
+#### [Pouch Datalog](https://github.com/dahjelle/pouch-datalog)
+
+Implement the Datalog query language on PouchDB, with indexes.
+
+#### [PouchDB Dump](https://github.com/nolanlawson/pouchdb-dump-cli) and [PouchDB Load](https://github.com/nolanlawson/pouchdb-load)
+
+Dump a PouchDB/CouchDB to a file, then load it wholesale. Designed for fast initial replication.
+
+#### [Delta Pouch](https://github.com/redgeoff/delta-pouch)
+
+Implements the handy "every document is a delta" pattern, so you don't have to deal with conflicts.
+
+#### [PouchDB Erase](https://github.com/marten-de-vries/pouchdb-erase)
+
+A replicating `db.destroy()` alternative.
+
+#### [PouchDB Full Sync](https://github.com/nolanlawson/pouchdb-full-sync)
+
+Fully replicate two PouchDB/CouchDB databases, preserving absolutely all revision history.
+
+#### [PouchDB GQL](https://github.com/pouchdb/GQL)
+
+Google Query Language (GQL) queries with PouchDB.
+
+#### [PouchDB Hoodie API](https://github.com/hoodiehq/pouchdb-hoodie-api)
+
+Hoodie-like API for PouchDB. ([Documentation](http://hoodiehq.github.io/pouchdb-hoodie-api/))
+
+#### [PouchDB Hoodie Store Client](https://www.npmjs.com/package/@hoodie/store-client)
+
+PouchDB Hoodie-like API for data persistence & offline sync.
+
+#### [PouchDB List](http://python-pouchdb.marten-de-vries.nl/plugins.html)
+
+Allows you to re-use your CouchDB list functions on the client side. ([Documentation](http://pythonhosted.org/Python-PouchDB/js-plugins.html#pouchdb-rewrite-plug-in))
+
+#### [Pouch Mirror](https://github.com/colinskow/pouch-mirror)
+
+Creates a synced in-memory mirror of a remote CouchDB for faster reads.
+
+#### [PouchDB No-Eval Map/Reduce](https://github.com/evidenceprime/pouchdb.mapreduce.noeval)
+
+Allows you to use the `query()` API in environments that disallow `eval()`, like Chrome packaged apps.
+
+#### [Peer Pouch](https://github.com/natevw/PeerPouch)
+
+PouchDB over WebRTC. (Note: only works with PouchDB 1.1.)
+
+#### [PouchDB Resolve Conflicts](https://github.com/jo/pouch-resolve-conflicts)
+
+Plugin to assist in PouchDB conflict resolving.
+
+#### [PouchDB Migrate](https://github.com/eHealthAfrica/pouchdb-migrate)
+
+PouchDB plugin for running data migrations.
+
+#### [PouchDB Quick Search](https://github.com/nolanlawson/pouchdb-quick-search)
+
+Full-text search engine on top of PouchDB.
+
+#### [Relational Pouch](https://github.com/nolanlawson/relational-pouch)
+
+A relational database API on top of PouchDB/CouchDB.
+
+#### [PouchDB Replication Stream](https://github.com/nolanlawson/pouchdb-replication-stream)
+
+Replicate between CouchDB/PouchDB using streams.
+
+#### [PouchDB Rewrite](http://python-pouchdb.marten-de-vries.nl/plugins.html)
+
+A PouchDB plugin that allows you to re-use your CouchDB rewrites on the client side. ([Documentation](http://pythonhosted.org/Python-PouchDB/js-plugins.html#pouchdb-list-plug-in))
+
+#### [PouchDB Show](http://python-pouchdb.marten-de-vries.nl/plugins.html)
+
+A PouchDB plugin that allows you to re-use your CouchDB show functions on the client side. ([Documentation](http://pythonhosted.org/Python-PouchDB/js-plugins.html#pouchdb-show-plug-in))
+
+#### [SocketPouch](https://github.com/nolanlawson/socket-pouch)
+
+PouchDB/CouchDB replication over WebSockets, using Engine.io (Socket.io).
+
+#### [PouchDB Spatial](https://github.com/pouchdb/geopouch)
+
+Multidimensional and spatial queries with PouchDB.
+
+#### [PouchDB Geospatial](https://github.com/dpmcmlxxvi/pouchdb-geospatial)
+
+PouchDB geospatial querying of GeoJSON objects that supports the DE-9IM spatial predicates. ([Documentation](https://dpmcmlxxvi.github.io/pouchdb-geospatial/api/))
+
+#### [Superlogin](https://www.npmjs.com/package/superlogin)
+
+Powerful authentication for APIs and single page apps using the CouchDB ecosystem, which supports a variety of providers.
+
+#### [Store.PouchDB](https://github.com/chunksnbits/store.pouchdb)
+
+ORM-style storage plugin for PouchDB.
+
+#### [Pouch Stream](https://github.com/calvinmetcalf/PouchStream)
+
+A plugin to let PouchDB talk streams.
+
+#### [Transform Pouch](https://github.com/nolanlawson/transform-pouch)
+
+Transforms documents before and after storage, e.g. for encryption, compression, or massaging data.
+
+
+#### [PouchDB Update](http://python-pouchdb.marten-de-vries.nl/plugins.html)
+
+A PouchDB plugin that allows you to re-use your CouchDB update functions on the client side. ([Documentation](http://pythonhosted.org/Python-PouchDB/js-plugins.html#pouchdb-update-plug-in))
+
+#### [PouchDB Upsert](https://github.com/nolanlawson/pouchdb-upsert)
+
+Convenience functions for working with documents: `upsert()` and `putIfNotExists()`.
+
+#### [PouchDB Validation](http://python-pouchdb.marten-de-vries.nl/plugins.html)
+
+A PouchDB plugin that allows you to re-use your CouchDB `validate_doc_update` functions on the client side. ([Documentation](http://pythonhosted.org/Python-PouchDB/js-plugins.html#pouchdb-validation-plug-in))
+
+#### [WorkerPouch](http://github.com/nolanlawson/worker-pouch)
+
+PouchDB adapter for web workers, so that PouchDB blocks the DOM less.
+
+{% include anchor.html title="Server Side" hash="Server Side" %}
+
+#### [PouchDB Server](https://github.com/pouchdb/pouchdb-server)
+
+A standalone CouchDB-style REST interface server to PouchDB.
+
+#### [Express PouchDB](https://github.com/pouchdb/express-pouchdb)
+
+An Express submodule with a CouchDB-style REST interface to PouchDB. Powers PouchDB Server.
+
+#### [Express PouchDB Replication Stream](https://github.com/conor-mac-aoidh/express-pouchdb-replication-stream)
+
+Server-side Express endpoint to deliver a stream from [PouchDB Replication Stream](https://github.com/nolanlawson/pouchdb-replication-stream).
+
+#### [Howler](https://github.com/redgeoff/couchdb-howler)
+
+Use web sockets to subscribe to CouchDB global changes
+
+#### [Pouch Websocket Sync](https://github.com/pgte/pouch-websocket-sync)
+
+Sync several PouchDBs through websockets. Supports reconnection, negotiation and authentication.
+
+#### [Pouch Remote Stream](https://github.com/pgte/pouch-remote-stream#readme)
+
+Consume a remote PouchDB stream. Goes well with [pouch-stream-server](https://github.com/pgte/pouch-stream-server#readme) on the server side.
+
+#### [Pouch Stream Server](https://github.com/pgte/pouch-stream-server#readme)
+
+PouchDB stream server. Serves generic PouchDB object streams. Goes well with [pouch-remote-stream](https://github.com/pgte/pouch-remote-stream#readme) on the client.
+
+#### [Spiegel](https://github.com/redgeoff/spiegel)
+
+Scalable replication and change listening for CouchDB
+
+{% include anchor.html title="Framework adapters" hash="framework_adapters" %}
+
+### Angular
+
+#### [angular-pouchdb](https://github.com/angular-pouchdb/angular-pouchdb)
+
+Wrapper for using PouchDB within Angular.js.
+
+#### [Factoryng - AngularJS Adapter](https://github.com/redgeoff/factoryng)
+
+An all-in-one AngularJS factory that wraps PouchDB and Delta Pouch.
+
+#### [ngPouch](https://github.com/jrhicks/ngPouch)
+
+Angular service to persist remote connection settings and maintain continuous replication.
+
+#### [ng-pouchdb](https://github.com/danielzen/ng-pouchdb)
+
+AngularJS binding for PouchDB.
+
+### Ampersand
+
+#### [ampersand-collection-pouchdb-mixin](https://github.com/svnlto/ampersand-collection-pouchdb-mixin)
+
+A mixin for extending ampersand-collection with pouchdb persistence.
+
+### Backbone
+
+#### [Backbone PouchDB](https://github.com/jo/backbone-pouch)
+
+Backbone PouchDB Sync Adapter.
+
+### Ember
+
+#### [Ember Pouch](https://github.com/nolanlawson/ember-pouch)
+
+Ember Data adapter for PouchDB/CouchDB.
+
+#### [ember-pouchdb](https://github.com/taras/ember-pouchdb)
+
+Promisy PouchDB wrapper for Ember.js.
+
+### [GopherJS](https://github.com/gopherjs/gopherjs)
+
+#### [Kivik](https://github.com/go-kivik/kivik)
+
+Kivik provides a common interface to CouchDB or CouchDB-like databases for Go and GopherJS. ([PouchDB driver](https://github.com/go-kivik/pouchdb))
+
+### Kendo UI
+
+#### [kendo-pouchdb](https://github.com/terikon/kendo-pouchdb)
+
+Kendo UI DataSource adapter.
+
+### React/Flux
+
+#### [react-pouchdb](https://github.com/ArnoSaine/react-pouchdb)
+
+React wrapper for PouchDB that also subscribes to changes.
+
+#### [pouch-redux](https://github.com/UXtemple/pouch-redux)
+
+Pouch and Redux integration. With Pouch in control this time around.
+
+#### [redux-pouchdb](https://github.com/vicentedealencar/redux-pouchdb)
+
+Sync store state to PouchDB.
+
+#### [redux-pouch](https://github.com/UXtemple/redux-pouch)
+
+PouchDB-backed Redux.
+
+#### [pouch-redux-middleware](https://github.com/pgte/pouch-redux-middleware#readme)
+
+Redux middleware to sync a PouchDB database with the Redux state.
+
+### Vue.js
+
+#### [pouch-vue](https://github.com/MDSLKTR/pouch-vue)
+
+Syncs PouchDB data with Vue.js components using Mango Selectors
+
+#### [vue-pouch-db](https://github.com/QurateInc/vue-pouch-db)
+
+Vue Pouch DB is a VueJS Plugin that binds PouchDB with Vue and keeps a synchronised state with the database. Has support for Mango queries which are processed locally within the VuePouchDB state.
+
+
+{% include anchor.html title="Other languages" hash="Other languages" %}
+
+#### [Python-PouchDB](http://python-pouchdb.marten-de-vries.nl/)
+A Python interface to PouchDB, with both a synchronous and an asynchronous API. Uses QtWebKit internally (via either PySide, PyQt4 or PyQt5). Some PouchDB plugins are also wrapped. ([Documentation](http://pythonhosted.org/Python-PouchDB/) / [Launchpad](https://launchpad.net/python-pouchdb))
+
+#### [PouchDroid](https://github.com/nolanlawson/PouchDroid/)
+
+Android adapter with a native Java interface to PouchDB.
+
+{% include anchor.html title="Tools" hash="Tools" %}
+
+#### [blob-util](https://github.com/nolanlawson/blob-util)
+
+Shims and utils for working with binary Blobs in the browser.
+
+#### [Pouchy](https://www.npmjs.com/package/pouchy)
+
+PouchDB sugar API. ([Github](https://github.com/cdaringe/pouchy))
+
+#### [Puton](http://puton.jit.su/)
+
+A bookmarklet for inspecting PouchDB databases within the browser. ([Github](http://github.com/ymichael/puton))
+
+#### [Revision Tree Visualizer](http://neojski.github.io/visualizeRevTree)
+
+A tool drawing revision tree of a couchdb document. You can see what is a conflict, which revisions are deleted and which is winning. ([Github](https://github.com/neojski/visualizeRevTree))
diff --git a/docs/version/9.0.0/faq.md b/docs/version/9.0.0/faq.md
new file mode 100644
index 0000000000..411ed8fc09
--- /dev/null
+++ b/docs/version/9.0.0/faq.md
@@ -0,0 +1,147 @@
+---
+layout: 2ColLeft.html
+title: FAQ
+sidebar: ./nav.html
+---
+
+{% include anchor.html class="h3" title="Can PouchDB sync with MongoDB/MySQL/my current non-CouchDB database?" hash="sync_non_couchdb" %}
+
+
+No, your backend needs to speak the [CouchDB replication protocol](http://couchdb.readthedocs.org/en/latest/replication/protocol.html). The magic of PouchDB <–> CouchDB sync comes from this design, which in particular requires all documents to be versioned with the `_rev` marker. This allows PouchDB and CouchDB to [elegantly handle conflicts](http://writing.jan.io/2013/12/19/understanding-couchdb-conflicts.html), among other benefits.
+
+{% include anchor.html class="h3" title="What can PouchDB sync with?" hash="what_can_pouchdb_sync_with" %}
+
+There are a number of databases that implement a CouchDB-like protocol, and PouchDB should be able to replicate with them. They include:
+
+ * [CouchDB](http://couchdb.apache.org/) – CouchDB is our primary reference database and is used for automated testing.
+ * [Cloudant](https://cloudant.com/) – A cluster-aware fork of CouchDB.
+ * [PouchDB Server](https://github.com/pouchdb/pouchdb-server) – An HTTP API written on top of PouchDB. Additionally, it supports alternate backends like in-memory, Redis, Riak and MySQL via [the LevelUP ecosystem](https://github.com/rvagg/node-levelup/wiki/Modules#storage). Note that your application must use the PouchDB API rather than directly modifying the database, however.
+
+{% include anchor.html class="h3" title="The web is nice, but I want to build a native app?" hash="native_support" %}
+
+PouchDB is one of multiple projects that implement the CouchDB protocol, and these can all be used to sync the same set of data.
+
+For desktop applications, you may want to look into embedding CouchDB (or [rcouch](https://github.com/refuge/rcouch)). PouchDB also works great with web-based frameworks like [node-webkit](https://github.com/rogerwang/node-webkit), [Chrome apps](https://developer.chrome.com/apps/about_apps), [Electron](https://github.com/atom/electron) and [WinJS](http://try.buildwinjs.com/#listview).
+
+For mobile applications, you can use PouchDB within [PhoneGap](http://phonegap.com/)/[Cordova](http://cordova.apache.org/) (optionally using the [SQLite Plugin](https://github.com/brodysoft/Cordova-SQLitePlugin)), or there are several native libraries:
+
+**iOS**:
+
+* [Couchbase Lite for iOS](https://github.com/couchbase/couchbase-lite-ios)
+* [Cloudant Sync for iOS](https://github.com/cloudant/CDTDatastore)
+
+**Android**:
+
+* [Couchbase Lite for Android](https://github.com/couchbase/couchbase-lite-android)
+* [Cloudant Sync for Android](https://github.com/cloudant/sync-android)
+
+{% include anchor.html class="h3" title="How much data can PouchDB store?" hash="data_limits" %}
+
+In **Firefox**, PouchDB uses IndexedDB. Though Firefox has no upper limit besides disk space, if your application wishes to store more than 50MB locally, Firefox will [ask the user](https://developer.mozilla.org/en-US/docs/Web/API/IndexedDB_API) using a non-modal dialog to confirm that this is okay.
+
+**Chrome** also uses IndexedDB, and it determines the amount of storage available on the user’s hard drive and uses that [to calculate a limit](https://developers.google.com/chrome/whitepapers/storage#temporary).
+
+**Opera 15+** shares a codebase with Chromium/Blink, and behaves similarly.
+
+**Internet Explorer 10+** has a hard 250MB limit, and will prompt the user with a non-modal dialog at 10MB.
+
+**Mobile Safari** on iOS has a hard 50MB limit for WebSQL, whereas **desktop Safari** has no limit. Both will prompt the user with a modal dialog if an application requests more than 5MB of data, at increments of 5MB, 10MB, 50MB, 100MB, etc. Some versions of Safari have a bug where they only let you request additional storage once, so you'll need to request the desired space up-front. PouchDB allows you to do this using [the `size` option]({{ site.baseurl }}/api.html#create_database).
+
+**iOS Web Application**, a page saved on the homescreen behaves differently than apps in Mobile Safari (at least from iOS 9.3.2+). No specifics are published online by Apple, but WebSQL storage seems not limited to 50mb and there will not be any prompts when requesting data storage. Use [``](https://developer.apple.com/library/ios/documentation/AppleApplications/Reference/SafariWebContent/ConfiguringWebApplications/ConfiguringWebApplications.html#//apple_ref/doc/uid/TP40002051-CH3-SW3) in your html header and use *Add to Home Screen* in the share menu of Safari. Please note, IndexedDB is now available as of iOS 10.3. It seems there is different behavior for different models of iPad and iPhone. You can check your mileage using the [storage abuser](http://demo.agektmr.com/storage/), which you can *Add to Home Screen* on your device. **Caveat**: when iOS is running low on storage space, the OS might decide to delete all data without any notice or warning to the end user. Be sure to use devices with plenty of spare space, or your users will lose unsynced data.
+
+**Android** works the same as Chrome as of 4.4+ (IndexedDB), while older versions can store up to 200MB (WebSQL).
+
+In [PhoneGap](http://phonegap.com/)/[Cordova](http://cordova.apache.org/), you can have unlimited data on both iOS and Android by using the [SQLite Plugin](https://github.com/brodysoft/Cordova-SQLitePlugin).
+
+For more information, see [Working with quota on mobile browsers](http://www.html5rocks.com/en/tutorials/offline/quota-research/).
+
+{% include anchor.html class="h3" title="What data types does PouchDB support?" hash="data_types" %}
+
+PouchDB has two types of data: documents and attachments.
+
+#### Documents
+
+As in CouchDB, the documents you store must be serializable as JSON. Modifying the `Object` prototype or storing classes is not supported.
+
+IndexedDB will actually support non-JSON data (e.g. `Date`s aren't stringified), but you should not rely on this, because CouchDB, LevelDB, and Web SQL do not behave the same.
+
+#### Attachments
+
+PouchDB also supports attachments, which are the most efficient way to store binary data. Attachments may either be supplied as base64-encoded strings or as `Blob` objects.
+
+Different backends have different strategies for storing binary data, which may affect the overall database size. Attachment stubs have a `length` property that describes the number of bytes in the `Blob` object, but under the hood, it may actually take up more space than that.
+
+PouchDB's strategies are:
+
+* **Blob**: data is stored in a true binary format. The most efficient method.
+* **UTF-16 Blob**: blobs are coerced to UTF-16, so they takes up 2x the normal space.
+* **Base-64**: data is stored as a base-64-encoded string. The least efficient method.
+
+Here are the strategies used by various browsers in PouchDB:
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Adapter
+
IE (10+)
+
Firefox
+
Chrome < 43, Android
+
Chrome >= 43
+
Safari < 7.1, iOS < 8
+
Safari < 10.1, >= 7.1, iOS < 10.3, >= 8
+
Safari >= 10.1, iOS >= 10.3
+
+
+
IndexedDB
+
Blob
+
Blob
+
Base-64
+
Blob
+
+
+
Blob
+
+
+
WebSQL
+
+
+
Blob
+
Blob
+
UTF-16 Blob
+
Blob
+
+
+
+
+
+Attachments are deduplicated based on their MD5 sum, so duplicate attachments won't take up extra space.
+
+To truly remove an attachment from the data store, you will need to use [compaction]({{ site.baseurl }}/api.html#compaction) to remove document revisions that reference that attachment.
+
+{% include anchor.html class="h3" title="Is it safe to upgrade PouchDB?" hash="safe_to_upgrade" %}
+
+Since v1.0.0, PouchDB has supported automatic schema migrations. This means that if you open a database that was built with an older version of PouchDB, the newer version will run all the steps necessary to get the old database up to date. We have extensive tests in place to guarantee that this feature works correctly.
+
+Even in the case of a major version release, PouchDB still performs the schema migrations. So for instance, you can create a database with PouchDB 1.0.0 and it will still work after you open it in 3.0.0.
+
+Once a database is migrated, however, you can no longer open it with an older version of PouchDB. So if an update contains a migration, it will be clearly marked in the release notes.
+
+{% include anchor.html class="h3" title="How is PouchDB different from CouchDB?" hash="couchdb_differences" %}
+
+PouchDB is also a CouchDB client, and you should be able to switch between a local database or an online CouchDB instance without changing any of your application's code.
+
+However, there are some minor differences to note:
+
+**View Collation** - CouchDB uses ICU to order keys in a view query; in PouchDB they are ASCII ordered.
+
+**View Offset** - CouchDB returns an `offset` property in the view results. In PouchDB, `offset` just mirrors the `skip` parameter rather than returning a true offset.
diff --git a/docs/version/9.0.0/getting-started.md b/docs/version/9.0.0/getting-started.md
new file mode 100644
index 0000000000..ea42b4df88
--- /dev/null
+++ b/docs/version/9.0.0/getting-started.md
@@ -0,0 +1,215 @@
+---
+layout: 2ColLeft.html
+title: Getting Started Guide
+sidebar: ./nav.html
+---
+
+{% include alert/start.html variant="info" %}
+
+If you get stuck, download a working version,
+or check out the full repo
+and commit history.
+
+{% include alert/end.html %}
+
+In this tutorial we will write a basic Todo web application based on [TodoMVC](http://todomvc.com/) that syncs to an online CouchDB server. It should take around 10 minutes.
+
+{% include anchor.html class="h3" title="Video Tutorial" hash="video_tutorial" %}
+
+Prefer video tutorials? This guide is available in video format:
+
+{% include iframe.html src="https://www.youtube.com/embed/-Z7UF2TuSp0" %}
+
+{% include anchor.html class="h3" title="Download Assets" hash="download" %}
+
+We will start with a template of the project where all the data related functions have been replaced with empty stubs. Download and unzip [pouchdb-getting-started-todo.zip](https://github.com/pouchdb/pouchdb-getting-started-todo/archive/master.zip). When dealing with XHR and IndexedDB you are better off running web pages from a server as opposed to a filesystem. To do this you can run:
+
+{% highlight bash %}
+$ cd pouchdb-getting-started-todo
+$ python -m SimpleHTTPServer # for Python 2
+$ python -m http.server # for Python 3
+{% endhighlight %}
+
+Then visit [http://127.0.0.1:8000/](http://127.0.0.1:8000/). If you see the following screenshot, you are good to go:
+
+{% include img.html src="screenshots/todo-1.png" alt="Todo Screenshot" %}
+
+It's also a good idea to open your browser's console so you can see any errors or confirmation messages.
+
+{% include anchor.html class="h3" title="Installing PouchDB" hash="installing_pouchdb" %}
+
+Open `index.html` and include PouchDB in the app by adding a script tag:
+
+{% highlight html %}
+
+
+
+{% endhighlight %}
+
+PouchDB is now installed in your app and ready to use! (In production, you should use a local copy of the script.)
+
+{% include anchor.html class="h3" title="Creating a database" hash="creating_a_database" %}
+
+The rest of the work will be done inside `app.js`. We will start by creating a database to enter your todos. To create a database simply instantiate a new PouchDB object with the name of the database:
+
+{% highlight js %}
+// EDITING STARTS HERE (you don't need to edit anything above this line)
+
+const db = new PouchDB('todos');
+const remoteCouch = false;
+{% endhighlight %}
+
+You don't need to create a schema for the database. After giving it a name, you can immediately start writing objects to it.
+
+{% include anchor.html class="h3" title="Write todos to the database" hash="write_todos_to_database" %}
+
+The first thing we shall do is start writing items to the database. The main input will call `addTodo` with the current text when the user presses `Enter`. We can complete this function with the following code:
+
+{% highlight js %}
+function addTodo(text) {
+ const todo = {
+ _id: new Date().toISOString(),
+ title: text,
+ completed: false
+ };
+ db.put(todo, function callback(err, result) {
+ if (!err) {
+ console.log('Successfully posted a todo!');
+ }
+ });
+}
+{% endhighlight %}
+
+In PouchDB each document is required to have a unique `_id`. Any subsequent writes to a document with the same `_id` will be considered updates. Here we are using a date string as an `_id`. For our use case, it will be unique, and it can also be used to sort items in the database. You can use `db.post()` if you want random ids. The `_id` is the only thing required when creating a new document. The rest of the object you can create as you like.
+
+The `callback` function will be called once the document has been written (or failed to write). If the `err` argument is not null, then it will have an object explaining the error, otherwise the `result` will hold the result.
+
+{% include anchor.html class="h3" title="Show items from the database" hash="show_database_items" %}
+
+We have included a helper function `redrawTodosUI` that takes an array of todos to display, so all we need to do is read the todos from the database. Here we will simply read all the documents using `db.allDocs`. The `include_docs` option tells PouchDB to give us the data within each document, and the `descending` option tells PouchDB how to order the results based on their `_id` field, giving us newest first.
+
+{% highlight js %}
+function showTodos() {
+ db.allDocs({include_docs: true, descending: true}, function(err, doc) {
+ redrawTodosUI(doc.rows);
+ });
+}
+{% endhighlight %}
+
+Once you have included this code, you should be able to refresh the page to see any todos you have entered.
+
+{% include anchor.html class="h3" title="Update the UI" hash="update_the_ui" %}
+
+We don't want to refresh the page to see new items. More typically you would update the UI manually when you write data to it, however, in PouchDB you may be syncing data remotely, so you want to make sure you update whenever the remote data changes. To do this we will call `db.changes` which subscribes to updates to the database, wherever they come from. You can enter this code between the `remoteCouch` and `addTodo` declaration:
+
+{% highlight js %}
+const remoteCouch = false;
+
+db.changes({
+ since: 'now',
+ live: true
+}).on('change', showTodos);
+
+// We have to create a new todo document and enter it in the database
+function addTodo(text) {
+{% endhighlight %}
+
+So every time an update happens to the database, we redraw the UI to show the new data. The `live` flag means this function will continue to run indefinitely. Now try entering a new todo and it should appear immediately.
+
+{% include anchor.html class="h3" title="Edit a todo" hash="edit_a_todo" %}
+
+When the user checks a checkbox, the `checkboxChanged` function will be called, so we'll fill in the code to edit the object and call `db.put`:
+
+{% highlight js %}
+function checkboxChanged(todo, event) {
+ todo.completed = event.target.checked;
+ db.put(todo);
+}
+{% endhighlight %}
+
+This is similar to creating a document, however the document must also contain a `_rev` field (in addition to `_id`), otherwise the write will be rejected. This ensures that you don't accidentally overwrite changes to a document.
+
+You can test that this works by checking a todo item and refreshing the page. It should stay checked.
+
+{% include anchor.html class="h3" title="Delete an object" hash="delete_an_object" %}
+
+To delete an object you can call db.remove with the object.
+
+{% highlight js %}
+function deleteButtonPressed(todo) {
+ db.remove(todo);
+}
+{% endhighlight %}
+
+Similar to editing a document, both the `_id` and `_rev` properties are required. You may notice that we are passing around the full object that we previously read from the database. You can of course manually construct the object, like: `{_id: todo._id, _rev: todo._rev}`, but passing around the existing object is usually more convenient and less error prone.
+
+{% include anchor.html class="h3" title="Complete rest of the Todo UI" hash="complete_todo_ui" %}
+
+`todoBlurred` is called when the user edits a document. Here we'll delete the document if the user has entered a blank title, and we'll update it otherwise.
+
+{% highlight js %}
+function todoBlurred(todo, event) {
+ const trimmedText = event.target.value.trim();
+ if (!trimmedText) {
+ db.remove(todo);
+ } else {
+ todo.title = trimmedText;
+ db.put(todo);
+ }
+}
+{% endhighlight %}
+
+{% include anchor.html class="h3" title="Installing CouchDB" hash="installing_couchdb" %}
+
+Now we'll implement the syncing. You need to have a compatible server instance. You can install either [PouchDB-Server](https://github.com/pouchdb/pouchdb-server), [CouchDB](http://couchdb.apache.org/) or use an hosted Couch service such as [Cloudant](https://cloudant.com/)
+
+{% include anchor.html class="h3" title="Enabling CORS" hash="enabling_cors" %}
+
+To replicate directly with CouchDB, you need to make sure CORS is enabled. Only set the username and password if you have set them previously. By default, CouchDB will be installed in "Admin Party," where username and password are not needed. You will need to replace `myname.example.com` with your own host (`127.0.0.1:5984` if installed locally):
+
+You can enable CORS in CouchDB using `curl` or the Futon web interface, but we've saved you some time by making a Node script called [add-cors-to-couchdb](https://github.com/pouchdb/add-cors-to-couchdb). Just run:
+
+{% highlight bash %}
+$ npm install -g add-cors-to-couchdb
+$ add-cors-to-couchdb
+{% endhighlight %}
+
+Or if your database is not at `127.0.0.1:5984`:
+
+{% highlight bash %}
+$ add-cors-to-couchdb http://me.example.com -u myusername -p mypassword
+{% endhighlight %}
+
+You can check that CORS is now enabled by visiting [http://localhost:5984/_utils/config.html](http://localhost:5984/_utils/config.html) in your browser. You should see something like this:
+
+{% include img.html src="cors_in_couchdb.png" alt="CORS settings in CouchDB" %}
+
+{% include anchor.html class="h3" title="Implement basic two way sync" hash="basic_two_way_sync" %}
+
+Now we will have the todo list sync. Back in `app.js` we need to specify the address of the remote database. Remember to replace `user`, `pass` and `myname.example.com` with the credentials of your own CouchDB instance:
+
+{% highlight js %}
+// EDITING STARTS HERE (you don't need to edit anything above this line)
+
+const db = new PouchDB('todos');
+const remoteCouch = 'http://user:pass@myname.example.com/todos';
+{% endhighlight %}
+
+Then we can implement the sync function like so:
+
+{% highlight js %}
+function sync() {
+ syncDom.setAttribute('data-sync-state', 'syncing');
+ const opts = {live: true};
+ db.replicate.to(remoteCouch, opts, syncError);
+ db.replicate.from(remoteCouch, opts, syncError);
+}
+{% endhighlight %}
+
+`db.replicate()` tells PouchDB to transfer all the documents `to` or `from` the `remoteCouch`. This can either be a string identifier or a PouchDB object. We call this twice: once to receive remote updates, and once to push local changes. Again, the `live` flag is used to tell PouchDB to carry on doing this indefinitely. The callback will be called whenever this finishes. For live replication, this will mean an error has occurred, like losing your connection or you canceled the replication.
+
+You should be able to open [the todo app](http://127.0.0.1:8000) in another browser and see that the two lists stay in sync with any changes you make to them. You may also want to look at your CouchDB's Futon administration page and see the populated database.
+
+{% include anchor.html class="h3" title="Congratulations!" hash="congratulations" %}
+
+You've completed your first PouchDB application. This is a basic example, and a real world application will need to integrate more error checking, user signup, etc. But you should now understand the basics you need to start working on your own PouchDB project. If you have any more questions, please get in touch on [IRC](ircs://irc.libera.chat:6697#pouchdb) [(web)](https://web.libera.chat/#pouchdb) or the [mailing list](https://groups.google.com/forum/#!forum/pouchdb).
diff --git a/docs/version/9.0.0/guides/async-code.md b/docs/version/9.0.0/guides/async-code.md
new file mode 100644
index 0000000000..9f087c88c2
--- /dev/null
+++ b/docs/version/9.0.0/guides/async-code.md
@@ -0,0 +1,211 @@
+---
+index: 6
+layout: guide.html
+title: Asynchronous code
+sidebar: guides_nav.html
+---
+
+PouchDB provides a fully **asynchronous** API. This ensures that when you talk to PouchDB, the UI doesn't stutter, because the DOM is not being blocked by database operations.
+
+However, working with asynchronous code can be very complex, especially if you're only accustomed to synchronous APIs. So it's worth going over some of the basics.
+
+{% include anchor.html title="I promise to call you back..." hash="i-promise-to-call-you-back" %}
+
+To make things as flexible as possible for PouchDB users, the API is provided in both **callback** format and **promise** format.
+
+The **callback** format looks like this:
+
+```js
+db.get('mittens', function (error, doc) {
+ if (error) {
+ // oh noes! we got an error
+ } else {
+ // okay, doc contains our document
+ }
+});
+```
+
+The **promise** format looks like this:
+
+```js
+db.get('mittens').then(function (doc) {
+ // okay, doc contains our document
+}).catch(function (err) {
+ // oh noes! we got an error
+});
+```
+
+Basically, if you include a callback as the last argument in a function, then PouchDB assumes you want the callback style. Otherwise it assumes you want the promise style.
+
+{% include anchor.html title="Let's talk about promises" hash="lets-talk-about-promises" %}
+
+For this guide, we will use the **promise** format for a few reasons:
+
+1. Callbacks easily lead to spaghetti code, or to the [pyramid of doom](https://medium.com/@wavded/managing-node-js-callback-hell-1fe03ba8baf).
+2. Promises generally lead to better code organization, although they do have a steep learning curve.
+
+If you already understand promises, you can [skip to the next section](updating-deleting.html).
+
+{% include alert/start.html variant="info"%}
+{% markdown %}
+
+**What about async/await?** Async functions are an experimental ES7 syntax that enhances promise-based APIs by adding
+the `async` and `await` keywords. For more information about `async`/`await`, read [our introductory blog post]({{ site.baseurl }}/2015/03/05/taming-the-async-beast-with-es7.html).
+
+{% endmarkdown %}
+{% include alert/end.html%}
+
+{% include anchor.html title="Understanding promises" hash="understanding-promises" %}
+
+If you have the time, you are strongly encouraged to watch [this 50-minute video: "Redemption from Callback Hell"](http://youtu.be/hf1T_AONQJU). The rest of this chapter basically summarizes that video.
+
+The best way to think of promises is that they bring keywords like `return` and `try/catch` to asynchronous code.
+
+Synchronous code:
+
+```js
+function returnSomething() {
+ try {
+ doSomething();
+ doSomethingElse();
+ return true;
+ } catch (err) {
+ console.log(err);
+ }
+}
+```
+
+Asynchronous code:
+
+```js
+function returnSomething() {
+ return doSomething().then(function () {
+ return doSomethingElse();
+ }).then(function () {
+ return true;
+ }).catch(function (err) {
+ console.log(err);
+ });
+}
+```
+
+{% include anchor.html title="Use `catch()` to catch errors" hash="use-catch-to catch errors" %}
+
+The big advantage of working with Promises in asynchronous code is that you can always attach a `catch` function to the end of a big promise chain, and any errors that occur along the way will show up at the end.
+
+This avoids endless `if (err) {}` checking in the callback world:
+
+```js
+doSomething(function (err, result) {
+ if (err) {
+ // handle error
+ }
+ doSomethingElse(function (err, result) {
+ if (err) {
+ // handle error again...
+ }
+ doSomethingYetAgain(function (err, result) {
+ if (err) {
+ // seriously? okay, handle error again...
+ }
+ });
+ });
+});
+```
+
+Instead, in the promise world, you can have a long chain of asynchronous operations with a single `catch` at the end. To use PouchDB as an example:
+
+```js
+db.put({_id: 'charlie', age: 21}).then(function () {
+ return db.get('charlie');
+}).then(function (charlie) {
+ // increment Charlie's age
+ charlie.age++;
+ return db.put(charlie);
+}).then(function () {
+ return db.get('charlie');
+}).then(function (charlie) {
+ // increment Charlie's age again
+ charlie.age++;
+ return db.put(charlie);
+}).then(function () {
+ return db.get('charlie');
+}).then(function (charlie) {
+ console.log(charlie);
+}).catch(function (err) {
+ console.log(err);
+});
+```
+
+You should see:
+
+```js
+{"age":23,"_id":"charlie","_rev":"3-e794618b4e39ed566cc68b56f5426e8e"}
+```
+
+You can see **[a live example](http://bl.ocks.org/nolanlawson/612f95cbbb69eaafc2d5)** of this code.
+
+In this example, we put/get a document 3 times in a row. At the very end, there is a `catch()` statement to catch any errors along the way.
+
+What kind of errors might we run into? Well, let's imagine that we accidentally misspell the id `'charlie'` at some point. In this case, we will gracefully catch the error. Here's another **[live example](http://bl.ocks.org/nolanlawson/0f1c815cb5fe74cff5fc)**.
+
+You should see:
+
+```js
+{"status":404,"name":"not_found","message":"missing"}
+```
+
+This is really nice! No matter where the misspelling is, the error can be handled within a single function. That's much nicer than having to do `if (err){}` an endless number of times!
+
+{% include anchor.html title="An alternate way of catching errors" hash="an-alternate-way-of-catching-errors" %}
+
+If you've been doing promises for awhile, you might have seen this instead:
+
+```js
+db.get('charlie').then(function (charlie) {
+ // we got the charlie doc
+}, function (err) {
+ // we got an error
+})
+```
+
+This is equivalent to:
+
+```js
+db.get('charlie').then(function (charlie) {
+ // we got the charlie doc
+}).catch(function (err) {
+ // we got an error
+})
+```
+
+The `catch()` method is just syntactic sugar. You can use either format.
+
+{% include anchor.html title="Promises 101" hash="promises-101" %}
+
+The `then()` method takes a function. What can you do within this function? Three things:
+
+* Return another promise
+* Throw an error
+* Return a non-promise object (or `undefined`)
+
+Another way to think of it is this:
+
+```js
+db.get('charlie').then(function (charlie) {
+ // Within this function, you can do
+ // try/catch/return like you normally would,
+ // and it will be handled asynchronously!
+}).then(function (result) {
+ // If the previous function returned something
+ // (or returned undefined), it will show up here
+ // as "result".
+}).catch(function (err) {
+ // If the previous function threw an error,
+ // it will show up here as "err".
+});
+```
+
+{% include anchor.html title="Next" hash="next" %}
+
+Now that you have a grasp on promises, let's learn about updating and deleting documents.
diff --git a/docs/version/9.0.0/guides/attachments.md b/docs/version/9.0.0/guides/attachments.md
new file mode 100644
index 0000000000..37c0fc37c7
--- /dev/null
+++ b/docs/version/9.0.0/guides/attachments.md
@@ -0,0 +1,314 @@
+---
+index: 9
+layout: guide.html
+title: Working with attachments
+sidebar: guides_nav.html
+---
+
+Attachments are where PouchDB can get really fun.
+
+The big difference between storage engines like WebSQL/IndexedDB and the older localStorage API is that you can stuff [a lot more data](https://web.dev/storage-for-the-web/) in it.
+
+PouchDB attachments allow you to use that to full advantage to store images, MP3s, zip files, or whatever you want.
+
+{% include anchor.html title="How attachments are stored" hash="how-attachments-are-stored" %}
+
+As their name implies, attachments are *attached* to documents. You can work with attachments either in base64-encoded format, or as a [Blob](https://developer.mozilla.org/en-US/docs/Web/API/Blob).
+
+For example, here is a very simple document with a plain text attachment, stored as base64.
+
+```js
+db.put({
+ _id: 'mydoc',
+ _attachments: {
+ 'myattachment.txt': {
+ content_type: 'text/plain',
+ data: 'aGVsbG8gd29ybGQ='
+ }
+ }
+});
+```
+
+Our document has the usual `_id` field, but it also has a special `_attachments` field that holds the attachments. Documents can have as many attachments as you want.
+
+{% include alert/start.html variant="info" %}
+
+When you create an attachment, you need to specify its content_type, otherwise known as the MIME type. Common MIME types include 'text/plain' for plain text, 'image/png' for PNG images, and 'image/jpeg' for JPG images.
+
+{% include alert/end.html %}
+
+As it turns out, `'aGVsbG8gd29ybGQ='` is just the string `'hello world'` encoded in base64. You can use the `atob()` and `btoa()` methods in your browser to verify.
+
+```js
+btoa('hello world') // "aGVsbG8gd29ybGQ="
+atob('aGVsbG8gd29ybGQ=') // "hello world"
+```
+
+Let's see what happens after we store this document. If you try to `get()` it normally, you may be surprised to see that the attachment data itself isn't returned:
+
+```js
+db.get('mydoc').then(function (doc) {
+ console.log(doc);
+});
+```
+
+The returned document will look like this:
+
+```js
+{
+ "_attachments": {
+ "myattachment.txt": {
+ "content_type": "text/plain",
+ "digest": "md5-XrY7u+Ae7tCTyyK7j1rNww==",
+ "stub": true
+ }
+ },
+ "_id": "mydoc",
+ "_rev": "1-e8a84187bb4e671f27ec11bdf7320aaa"
+}
+```
+
+You can see **[a live example](http://bl.ocks.org/nolanlawson/0a4b1267d3a5b5edd7b1)** of this code.
+
+By default, PouchDB will only give you an attachment **stub**, which contains a `digest`, i.e. the [md5sum](http://en.wikipedia.org/wiki/Md5sum) of the binary attachment.
+
+To get the full attachments when using `get()` or `allDocs()`, you need to specify `{attachments: true}`:
+
+```js
+db.get('mydoc', {attachments: true}).then(function (doc) {
+ console.log(doc);
+});
+```
+
+Then you'll get back the full attachment, base64-encoded:
+
+```js
+{
+ "_attachments": {
+ "myattachment.txt": {
+ "content_type": "text/plain",
+ "digest": "md5-XrY7u+Ae7tCTyyK7j1rNww==",
+ "data": "aGVsbG8gd29ybGQ="
+ }
+ },
+ "_id": "mydoc",
+ "_rev": "1-e8a84187bb4e671f27ec11bdf7320aaa"
+}
+```
+
+You can see **[a live example](http://bl.ocks.org/nolanlawson/b6d6164035f1fa0d38a8)** of this code.
+
+{% include anchor.html title="Image attachments" hash="image-attachments" %}
+
+Plaintext is cool and all, but you know what would be *really* awesome? Storing images.
+
+So let's do it! In this example, we'll put a document with a small icon attachment, represented as a base64-encoded string. Then we'll fetch it and display the icon as a normal `` tag:
+
+```js
+db.put({
+ _id: 'meowth',
+ _attachments: {
+ 'meowth.png': {
+ content_type: 'image/png',
+ data: 'iVBORw0KGgoAAAANSUhEUgAAACgAAAAkCAIAAAB0Xu9BAAAABGdBTUEAALGPC/xhBQAAAuNJREFUWEetmD1WHDEQhDdxRMYlnBFyBIccgdQhKVcgJeQMpE5JSTd2uqnvIGpVUqmm9TPrffD0eLMzUn+qVnXPwiFd/PP6eLh47v7EaazbmxsOxjhTT88z9hV7GoNF1cUCvN7TTPv/gf/+uQPm862MWTL6fff4HfDx4S79/oVAlAUwqOmYR0rnazuFnhfOy/ErMKkcBFOr1vOjUi2MFn4nuMil6OPh5eGANLhW3y6u3aH7ijEDCxgCvzFmimvc95TekZLyMSeJC68Bkw0kqUy1K87FlpGZqsGFCyqEtQNDdFUtFctTiuhnPKNysid/WFEFLE2O102XJdEE+8IgeuGsjeJyGHm/xHvQ3JtKVsGGp85g9rK6xMHtvHO9+WACYjk5vkVM6XQ6OZubCJvTfPicYPeHO2AKFl5NuF5UK1VDUbeLxh2BcRGKTQE3irHm3+vPj6cfCod50Eqv5QxtwBQUGhZhbrGVuRia1B4MNp6edwBxld2sl1splfHCwfsvCZfrCQyWmX10djjOlWJSSy3VQlS6LmfrgNvaieRWx1LZ6s9co+P0DLsy3OdLU3lWRclQsVcHJBcUQ0k9/WVVrmpRzYQzpgAdQcAXxZzUnFX3proannrYH+Vq6KkLi+UkarH09mC8YPr2RMWOlEqFkQClsykGEv7CqCUbXcG8+SaGvJ4a8d4y6epND+pEhxoN0vWUu5ntXlFb5/JT7JfJJqoTdy9u9qc7ax3xJRHqJLADWEl23cFWl4K9fvoaCJ2BHpmJ3s3z+O0U/DmzdMjB9alWZtg4e3yxzPa7lUR7nkvxLHO9+tvJX3mtSDpwX8GajB283I8R8a7D2MhUZr1iNWdny256yYLd52DwRYBtRMvE7rsmtxIUE+zLKQCDO4jlxB6CZ8M17GhuY+XTE8vNhQiIiSE82ZsGwk1pht4ZSpT0YVpon6EvevOXXH8JxVR78QzNuamupW/7UB7wO/+7sG5V4ekXb4cL5Lyv+4IAAAAASUVORK5CYII='
+ }
+ }
+}).then(function () {
+ return db.getAttachment('meowth', 'meowth.png');
+}).then(function (blob) {
+ const url = URL.createObjectURL(blob);
+ const img = document.createElement('img');
+ img.src = url;
+ document.body.appendChild(img);
+}).catch(function (err) {
+ console.log(err);
+});
+```
+
+You can see **[a live example](http://bl.ocks.org/nolanlawson/2a5f98a66c9fe3ae3532)** of this code.
+
+You should be unsurprised to see a cat smiling back at you. If the kitten theme bothers you, then you haven't been on the Internet very long.
+
+How does this code work? First off, we are making use of the `URL.createObjectURL()` method, which is a standard HTML5 method that converts a `Blob` to a URL that we can easily use as the `src` of an `img`.
+
+Second off, we are using the `getAttachment()` API, which returns a `Blob` rather than a base64-encoded string. To be clear: we can always convert between base64 and `Blob`s, but in this case, `getAttachment()` is just more convenient.
+
+
+{% include anchor.html title="Directly storing binary data" hash="directly-storing-binary-data" %}
+
+Up to now, we've been supplying our attachments as base64-encoded strings. But we can also create the Blobs ourselves and store those directly in PouchDB.
+
+Another shortcut we can use is the `putAttachment()` API, which simply modifies the existing document to hold a new attachment. Or, if the document does not exist, it will create an empty one.
+
+{% include alert/start.html variant="info" %}
+
+In Node.js, PouchDB uses Buffers instead of Blobs. Otherwise, the same rules apply.
+
+{% include alert/end.html %}
+
+For instance, we can read the image data from an `` tag using a `canvas` element, and then directly write that Blob to PouchDB:
+
+```js
+function convertImgToBlob(img, callback) {
+ const canvas = document.createElement('canvas');
+ const context = canvas.getContext('2d');
+ context.drawImage(img, 0, 0);
+
+ // Warning: toBlob() isn't supported by every browser.
+ // You may want to use blob-util.
+ canvas.toBlob(callback, 'image/png');
+}
+
+const catImage = document.getElementById('cat');
+convertImgToBlob(catImage, function (blob) {
+ db.putAttachment('meowth', 'meowth.png', blob, 'image/png').then(function () {
+ return db.get('meowth', {attachments: true});
+ }).then(function (doc) {
+ console.log(doc);
+ });
+});
+```
+
+You can see **[a live example](http://bl.ocks.org/nolanlawson/edaf09b84185418a55d9)** of this code.
+
+This stores exactly the same image content as in the other example, which you can confirm by checking the base64-encoded output.
+
+{% include alert/start.html variant="warning" %}
+
+Blobs can be tricky to work with, especially when it comes to cross-browser support.
+You may find blob-util to be a useful
+addition to the attachment API. For instance, it has an
+imgSrcToBlob() method that will work cross-browser.
+
+{% include alert/end.html %}
+
+{% include anchor.html title="Allow the user to store an attachment" hash="allow-the-user-to-store-an-attachment" %}
+
+You can also upload a file with the HTML5 `File` API and store it directly in the database, because the data you get from the `` element is already a `Blob`.
+(See: [Blob API](https://developer.mozilla.org/en-US/docs/Web/API/Blob) and [File API](https://developer.mozilla.org/en-US/docs/Web/API/File), which inherits properties from the `Blob` Interface.)
+
+Here is an example of allowing a user to choose a file from their filesystem:
+
+```html
+
+```
+
+And then "uploading" that file directly into PouchDB:
+
+```js
+const input = document.querySelector('input');
+input.addEventListener('change', function () {
+ const file = input.files[0]; // file is a Blob
+
+ db.put({
+ _id: 'mydoc',
+ _attachments: {
+ filename: {
+ content_type: file.type,
+ data: file
+ }
+ }
+ }).catch(function (err) {
+ console.log(err);
+ });
+});
+```
+
+You can see **[a live example](http://bl.ocks.org/ntwcklng/f57b03c15e91c25e2cb5)** of this code.
+
+Select a file and you will see the stored file, `size`, and `type`, which are valid `Blob` properties. If you choose an image, it will also show the image!
+
+{% include anchor.html title="Base64 vs Blobs/Buffers" hash="base64-vs-blobs-buffers" %}
+
+Whether you supply attachments as base64-encoded strings or as Blobs/Buffers, PouchDB will try to store them in [the most efficient way](/faq.html#data_types).
+
+So when you insert your attachments, either format is acceptable. For instance, you can put Blobs/Buffers using `put()`:
+
+```js
+db.put({
+ _id: 'mydoc',
+ _attachments: {
+ 'myattachment.txt': {
+ content_type: 'text/plain',
+ data: myBlob
+ }
+ }
+});
+```
+
+And you can also pass base64-encoded strings to `putAttachment()`:
+
+```js
+db.putAttachment('mydoc', 'myattachment.png', myBase64String, 'image/png');
+```
+
+You can also insert multiple attachments at once using `put()`:
+
+```js
+db.put({
+ _id: 'mydoc',
+ _attachments: {
+ 'myattachment1.txt': {
+ content_type: 'text/plain',
+ data: myBlob1
+ },
+ 'myattachment2.txt': {
+ content_type: 'text/plain',
+ data: myBlob2
+ },
+ 'myattachment3.txt': {
+ content_type: 'text/plain',
+ data: myBlob3
+ },
+ // etc.
+ }
+});
+```
+
+The `bulkDocs()` and `post()` APIs also accept attachments in either format.
+
+When you fetch attachments, however, `getAttachment()` will always return Blobs/Buffers.
+
+The other "read" APIs, such as `get()`, `allDocs()`, `changes()`, and `query()` have an `{attachments: true}` option that returns the attachments base64-encoded strings. If you add `{binary: true}`, though, they will return Blobs/Buffers.
+
+{% include alert/start.html variant="info" %}
+{% markdown %}
+
+**Performance tip:** If you can insert and retrieve your attachments using _only_ Blobs/Buffers, then you will typically get better performance, especially when it comes to memory usage. The base64 string format is mostly provided for developer convenience and debugging.
+
+{% endmarkdown %}
+{% include alert/end.html %}
+
+
+{% include anchor.html title="Blob types" hash="blob-types" %}
+
+Blobs have their own `type`, but there is also a `content_type` that you specify when you store it in PouchDB:
+
+```js
+const myBlob = new Blob(['I am plain text!'], {type: 'text/plain'});
+console.log(myBlob.type); // 'text/plain'
+
+db.put({
+ _id: 'mydoc',
+ _attachments: {
+ 'myattachment.txt': {
+ content_type: 'text/plain',
+ data: myBlob
+ }
+ }
+});
+```
+
+The reason for this redundancy is 1) Buffers in Node do not have a `type`, and 2) the CouchDB attachment format requires it.
+
+So for best results, you should ensure that your Blobs have the same type as the one reported to PouchDB. Otherwise you may see inconsistent behavior (e.g. in IndexedDB, where the Blob is stored as-is on compatible browsers).
+
+{% include anchor.html title="Related API documentation" hash="related-api-documentation" %}
+
+* [putAttachment()](/api.html#save_attachment)
+* [getAttachment()](/api.html#get_attachment)
+* [removeAttachment()](/api.html#delete_attachment)
+
+{% include anchor.html title="Next" hash="next" %}
+
+Now that you can attach cat pictures to all your documents (and why wouldn't you?), let's talk about replication.
diff --git a/docs/version/9.0.0/guides/bulk-operations.md b/docs/version/9.0.0/guides/bulk-operations.md
new file mode 100644
index 0000000000..28ae8634fe
--- /dev/null
+++ b/docs/version/9.0.0/guides/bulk-operations.md
@@ -0,0 +1,132 @@
+---
+index: 8
+layout: guide.html
+title: Bulk operations
+sidebar: guides_nav.html
+---
+
+You can `get()`, `put()`, and `remove()` single documents to your heart's content, but a database isn't a database unless it can handle many operations at once!
+
+PouchDB provides two methods for bulk operations - `bulkDocs()` for bulk writes, and `allDocs()` for bulk reads.
+
+{% include anchor.html title="Use `bulkDocs()` to write many docs" hash="use-bulkdocs-to-write-many-docs" %}
+
+The `bulkDocs()` API is very simple. It just takes a list of documents that you want to `put()` into the database:
+
+```js
+db.bulkDocs([
+ {
+ _id: 'mittens',
+ occupation: 'kitten',
+ cuteness: 9.0
+ },
+ {
+ _id: 'katie',
+ occupation: 'kitten',
+ cuteness: 7.0
+ },
+ {
+ _id: 'felix',
+ occupation: 'kitten',
+ cuteness: 8.0
+ }
+]);
+```
+
+You can see **[a live example](http://bl.ocks.org/nolanlawson/038a45134341f3b7235b)** of this code.
+
+This code is equivalent to `put()`ing each document separately:
+
+```js
+db.put({
+ _id: 'mittens',
+ occupation: 'kitten',
+ cuteness: 9.0
+}).then(function () {
+ return db.put({
+ _id: 'katie',
+ occupation: 'kitten',
+ cuteness: 7.0
+ });
+}).then(function () {
+ return db.put({
+ _id: 'felix',
+ occupation: 'kitten',
+ cuteness: 8.0
+ });
+});
+```
+
+{% include anchor.html title="Why bulk up with `bulkDocs()`?" hash="why-bulk-up-with-bulkdocs" %}
+
+Bulk operations tend to be faster than individual operations, because they can be combined into a single transaction (in a local IndexedDB/WebSQL) or a single HTTP request (in a remote CouchDB).
+
+You can also update or delete multiple documents this way. You just need to include the `_rev` and `_deleted` values as previously discussed. The same rules as for `put()` apply to each individual document.
+
+{% include alert/start.html variant="warning" %}
+
+Neither bulkDocs() nor allDocs() constitutes a transaction in the traditional sense. That means that, if a single put() fails, you should not assume that the others will fail.
+
+
+By design, CouchDB and PouchDB do not support transactions. A document is the smallest unit of operations.
+{% include alert/end.html %}
+
+{% include anchor.html title="Use `allDocs()` to read many docs" hash="use-alldocs-to-read-many-docs" %}
+
+Likewise, `allDocs()` is a method that allows you to read many documents at once.
+
+Most crucially, when you read from `allDocs()`, the documents are returned *sorted by order of `_id`*. This makes the `_id` a very powerful field that you can use for more than just uniquely identifying your documents.
+
+For instance, if you refer back to [the live example](http://bl.ocks.org/nolanlawson/038a45134341f3b7235b) above, you'll notice that the kittens are sorted by their name, because their names are used as their `_id`s.
+
+Another common way to take advantage of this is to use `new Date().toJSON()` as your document `_id`s. In this way, all your documents will be sorted by date.
+
+For instance, let's save three kittens with three different dates, and then fetch them sorted by date:
+
+```js
+db.put({
+ _id: new Date().toJSON(),
+ name: 'Mittens',
+ occupation: 'kitten',
+ cuteness: 9.0
+}).then(function () {
+ return db.put({
+ _id: new Date().toJSON(),
+ name: 'Katie',
+ occupation: 'kitten',
+ cuteness: 7.0
+ });
+}).then(function () {
+ return db.put({
+ _id: new Date().toJSON(),
+ name: 'Felix',
+ occupation: 'kitten',
+ cuteness: 8.0
+ });
+}).then(function () {
+ return db.allDocs({include_docs: true});
+}).then(function (response) {
+ console.log(response);
+}).catch(function (err) {
+ console.log(err);
+});
+```
+
+You can see **[a live example](http://bl.ocks.org/nolanlawson/8f58dbc360348a4c95f6)** to confirm that the kittens are sorted by the order they were put into the database.
+
+{% include anchor.html title="Please use `allDocs()`. Seriously." hash="please-use-alldocs" %}
+
+`allDocs()` is the unsung star of the PouchDB world. It not only returns documents in order – it also allows you to reverse the order, filter by `_id`, slice and dice using "greater than" and "less than" operations on the `_id`, and much more.
+
+Far too many developers overlook this valuable API, because they misunderstand it. When a developer says "my PouchDB app is slow!", it is usually because they are using the slow `query()` API when they should be using the fast `allDocs()` API.
+
+For details on how to effectively use `allDocs()`, you are strongly recommended to read ["Pagination strategies with PouchDB"]({{ site.baseurl }}/2014/04/14/pagination-strategies-with-pouchdb.html). For 99% of your applications, you should be able to use `allDocs()` for all the pagination/sorting/searching functionality that you need.
+
+{% include anchor.html title="Related API documentation" hash="related-api-documentation" %}
+
+* [bulkDocs()](/api.html#batch_create)
+* [allDocs()](/api.html#batch_fetch)
+
+{% include anchor.html title="Next" hash="next" %}
+
+Now that you've fallen helplessly in love with `bulkDocs()` and `allDocs()`, let's turn our wandering gaze to attachments.
diff --git a/docs/version/9.0.0/guides/changes.md b/docs/version/9.0.0/guides/changes.md
new file mode 100644
index 0000000000..0bc3168016
--- /dev/null
+++ b/docs/version/9.0.0/guides/changes.md
@@ -0,0 +1,142 @@
+---
+index: 12
+layout: guide.html
+title: Changes feed
+sidebar: guides_nav.html
+---
+
+One of the brilliant things about CouchDB replication is that it makes it easy to learn about changes made to the database over time. CouchDB allows you to easily answer questions like:
+
+* What changes occurred to the database since a given time?
+* What changes occurred to this document?
+* What did this database look like a few days ago?
+
+For all of these and related questions, there's the `changes()` API.
+
+{% include anchor.html title="Basic changes usage" hash="basic-changes-usage" %}
+
+If you want to simply fetch all changes since the beginning of time, you can do:
+
+```js
+db.changes({
+ since: 0,
+ include_docs: true
+}).then(function (changes) {
+
+}).catch(function (err) {
+ // handle errors
+});
+```
+
+You can see a **[live example](http://bl.ocks.org/nolanlawson/7c32861af5d31a8fac4a)** of this code.
+
+Then you will have a list of all changes made to the database, in the order that they were made.
+
+One thing you will notice about the changes feed is that it actually omits non-leaf revisions to documents. For instance, in the live example, we skip from `seq` 1 immediately to `seq` 3.
+
+This is by design – the changes feed only tells us about leaf revisions. However, the order of those leaf revisions is determined by the order they were put in the database. So you may notice that `'firstDoc'` still appears before `'secondDoc'`, which appears before `'thirdDoc'`.
+
+Also notice the option `{include_docs: true}`. By default, the documents themselves are not included in the changes feed; only the `id`s, `rev`s, and whether or not they were `deleted`. With `{include_docs: true}`, however, each non-deleted change will have a `doc` property containing the new or modified document.
+
+{% include anchor.html title="Changes pagination" hash="changes-pagination" %}
+
+If you expect this to be a very large number of changes, you can also use the `limit` option to do pagination:
+
+```js
+const pageSize = 10;
+let lastSeq = 0;
+function fetchNextPage() {
+ return db.changes({
+ since: lastSeq,
+ limit: pageSize
+ }).then(function (changes) {
+ if (changes.results.length < pageSize) {
+ // done!
+ } else {
+ lastSeq = changes.last_seq;
+ return fetchNextPage();
+ }
+ });
+}
+
+fetchNextPage().catch(function (err) {
+ // handle errors
+});
+```
+
+You can see a **[live example](http://bl.ocks.org/nolanlawson/dcdeae555b31c2a6d332)** of this code.
+
+{% include anchor.html title="`seq` versus `_rev`" hash="seq-versus-rev" %}
+
+The changes feed lists each change with a corresponding `seq` integer. `seq` always starts with 0, and beyond that it increases monotonically. (In Cloudant, these are strings rather than integers.)
+
+`seq` can be thought of as a version number for the entire database. Basically it answers the question of "How many total changes have been made to all documents in this database?" This sets it apart from the revision hash `_rev`, which marks the changes made to a single document.
+
+However, the `seq` between two databases is not guaranteed to be kept in sync. CouchDB and PouchDB have slightly different ways of increasing their `seq` values, so really `seq` is only meaningful within a single database.
+
+{% include anchor.html title="Live changes feed" hash="live-changes-feed" %}
+
+Just like replication, you can also listen to a live changes feed. The way this works is very similar to the replication API:
+
+```js
+db.changes({
+ since: 'now'
+}).on('change', function (change) {
+ // received a change
+}).on('error', function (err) {
+ // handle errors
+});
+```
+
+In the above example, we've also taken advantage of the quasi-magical `'now'` option for `since`, which will give us all changes from the moment we start listening.
+
+This can be very useful for scenarios where you want to update the UI whenever something in the database changes, such as for a real-time chat application.
+
+{% include anchor.html title="Understanding changes" hash="understanding-changes" %}
+
+There are two types of changes:
+
+* Added or modified documents
+* Deleted documents
+
+To distinguish between the two types in a live `changes()` listener,
+you can use the following code:
+
+```js
+db.changes({
+ since: 'now',
+ live: true,
+ include_docs: true
+}).on('change', function (change) {
+ // change.id contains the doc id, change.doc contains the doc
+ if (change.deleted) {
+ // document was deleted
+ } else {
+ // document was added/modified
+ }
+}).on('error', function (err) {
+ // handle errors
+});
+```
+
+You can see a [live example](http://bl.ocks.org/nolanlawson/fa42662cdfeeaa7b78fc) of this code.
+
+Notice that `change.doc` contains the document (unless it's deleted), because we used `{include_docs: true}`.
+
+Also notice that new documents always have revisions starting with the string `'1-'`. Subsequent revisions start with `'2-'`, `'3-'`, `'4-'`, etc.
+
+{% include alert/start.html variant="info" %}
+
+
How can I distinguish between added and modified documents? Checking if the revision starts with '1-' is a pretty good trick. However, this will not work for databases that are replication targets, because replication only sends the latest versions of documents. This means that the '1-' revision may get skipped entirely, and the local database will only receive the 2nd, 3rd or 4th (etc.) revision. Conflicting revisions will also appear in the changes feed.
+
+
So the short answer is that you cannot. If you are trying to mirror changes in a non-Pouch structure (e.g. a list of DOM elements), then the best solution is to search all the DOM elements to see if the document already exists, or to re-run allDocs() for every change.
+
+{% include alert/end.html %}
+
+{% include anchor.html title="Related API documentation" hash="related-api-documentation" %}
+
+* [changes()](/api.html#changes)
+
+{% include anchor.html title="Next" hash="next" %}
+
+Now that we know how to hook our data spigot to the changes feed, let's look into using the very powerful `find()` API.
diff --git a/docs/version/9.0.0/guides/compact-and-destroy.md b/docs/version/9.0.0/guides/compact-and-destroy.md
new file mode 100644
index 0000000000..2c9ed1a9d2
--- /dev/null
+++ b/docs/version/9.0.0/guides/compact-and-destroy.md
@@ -0,0 +1,97 @@
+---
+index: 15
+layout: guide.html
+title: Compacting and destroying
+sidebar: guides_nav.html
+---
+
+By default, PouchDB and CouchDB are designed to store all document revisions forever. This is very similar to how Git works, and it helps ensure that two databases can consistently replicate with each other.
+
+However, if you allow your database to grow without bounds, it can end up taking up much more space than you need. This can especially be a problem in [browsers with storage quotas](/faq.html#data_limits).
+
+To mitigate this problem, PouchDB offers two recourses: compaction and destruction.
+
+{% include anchor.html title="Compacting a database" hash="compacting-a-database" %}
+
+When you compact a database, you tell PouchDB to optimize its current storage usage. CouchDB will do the same thing:
+
+```js
+return db.compact().then(function (info) {
+ // compaction complete
+}).catch(function (err) {
+ // handle errors
+});
+```
+
+From the API perspective, nothing should be different about the database after compaction, *except* that non-leaf revisions will no longer be available.
+
+```js
+db.put({_id: 'foo', version: 1}).then(function () {
+ return db.get('foo');
+}).then(function (doc) {
+ doc.version = 2;
+ return db.put(doc);
+}).then(function () { )
+ return db.compact();
+}).then(function () {
+ // DANGER!
+ // From now on, revision 1 is no longer available.
+}).catch(function (err) {
+ // handle errors
+});
+```
+
+You can see a **[live example](http://bl.ocks.org/nolanlawson/ff6eb521793e3a199864)** of this code.
+
+Compaction is a great feature, but it may not be what you desire if you want to retain a document's history from the beginning of time.
+
+However, if that's not a concern, then compaction is a harmless operation. In fact, since leaf revisions are retained, this means that you can still do [conflict resolution](/guides/conflicts.html) after compaction!
+
+{% include anchor.html title="Auto-compaction" hash="auto-compaction" %}
+
+If you really want to go all-in on compaction, then you can even put your database in `auto_compaction` mode. This means that it will automatically perform a `compact()` operation after every write.
+
+```js
+const db = new PouchDB('mydb', {auto_compaction: true});
+db.put({_id: 'foo', version: 1}).then(function () {
+ return db.get('foo');
+}).then(function (doc) {
+ doc.version = 2;
+ return db.put(doc);
+}).then(function () {
+ // Revision 1 is already unavailable!
+}).catch(function (err) {
+ // handle errors
+});
+```
+
+You can see a **[live example](http://bl.ocks.org/nolanlawson/b88f46d7cbaef8d93cba)** of this code.
+
+This feature is only available in local databases, not remote ones. On remote databases, the `auto_compaction` option will do nothing.
+
+{% include anchor.html title="Destroying a database" hash="destroying-a–database" %}
+
+We all love our databases, but sometimes good things must come to an end, and you need to snub out a database completely.
+
+So if you want to give your database to a nice farm family upstate, then the `destroy()` API is for you. It's very simple:
+
+```js
+new PouchDB('mydb').destroy().then(function () {
+ // database destroyed
+}).catch(function (err) {
+ // error occurred
+})
+```
+
+Note that destroying a database does not mean that replicated databases will also be destroyed. Destruction has nothing to do with the normal `put()`/`remove()` operations on documents, so it has no impact on replication.
+
+Also note that in Web SQL, the database will not really be destroyed – it will just have its tables dropped. This is because Web SQL does not support true database deletion.
+
+{% include anchor.html title="Related API documentation" hash="related-api-documentation" %}
+
+* [compact()](/api.html#compaction)
+* [destroy()](/api.html#delete_database)
+
+{% include anchor.html title="Next" hash="next" %}
+
+To wrap up, let's look at a special class of documents in PouchDB – local docs.
diff --git a/docs/version/9.0.0/guides/conflicts.md b/docs/version/9.0.0/guides/conflicts.md
new file mode 100644
index 0000000000..60898fe779
--- /dev/null
+++ b/docs/version/9.0.0/guides/conflicts.md
@@ -0,0 +1,160 @@
+---
+index: 11
+layout: guide.html
+title: Conflicts
+sidebar: guides_nav.html
+---
+
+Conflicts are an unavoidable reality when dealing with distributed systems. And make no mistake: client-server *is* a distributed system.
+
+CouchDB and PouchDB differ from many other sync solutions, because they bring the issue of conflicts front-and-center. With PouchDB, conflict resolution is entirely under your control.
+
+{% include alert/start.html variant="info" %}
+
+PouchDB exactly implements CouchDB's replication algorithm, so conflict resolution works the same in both. For the purposes of this article, "CouchDB" and "PouchDB" may be used interchangeably.
+
+{% include alert/end.html %}
+
+{% include anchor.html title="Two types of conflicts" hash="two-types-of-conflicts" %}
+
+In CouchDB, conflicts can occur in two places: immediately, when you try to commit a new revision, or later, when two peers have committed changes to the same document. Let's call these **immediate conflicts** and **eventual conflicts**.
+
+### Immediate conflicts
+
+**Immediate conflicts** can occur with any API that takes a `rev` or a document with `_rev` as input – `put()`, `post()`, `remove()`, `bulkDocs()`, and `putAttachment()`. They manifest as a `409` (conflict) error:
+
+```js
+const myDoc = {
+ _id: 'someid',
+ _rev: '1-somerev'
+};
+db.put(myDoc).then(function () {
+ // success
+}).catch(function (err) {
+ if (err.name === 'conflict') {
+ // conflict!
+ } else {
+ // some other error
+ }
+});
+```
+
+In your code, *you should always be handling conflicts*. No matter how unlikely it may seem, 409s can and do occur.
+
+For instance, if you are doing live replication, a document may be modified by somebody else while the user is working on it. If the remote changes are replicated to the local database before the user tries to commit their changes, then they will receive the above 409 error.
+
+#### Upsert
+
+In many cases, the most practical solution to the 409 problem is to retry the `put()` until it succeeds. If the user's intended change can be expressed as a **delta** (i.e. a change that doesn't depend on the current revision), then this is very easy to achieve.
+
+Borrowing a phrase from traditional databases, let's call this an **upsert** ("update or insert"), and use the [pouchdb-upsert](https://github.com/pouchdb/pouchdb-upsert) plugin to implement it:
+
+```js
+function myDeltaFunction(doc) {
+ doc.counter = doc.counter || 0;
+ doc.counter++;
+ return doc;
+}
+
+db.upsert('my_id', myDeltaFunction).then(function () {
+ // success!
+}).catch(function (err) {
+ // error (not a 404 or 409)
+});
+```
+
+This `upsert()` function takes a `docId` and `deltaFunction`, where the `deltaFunction` is just a function that takes a document and outputs a new document. (If the document does not exist, then an empty document is provided.)
+
+`pouchdb-upsert` also offers a `putIfNotExists()` function, which will create a document if it doesn't exist already. For more details, see [the plugin's documentation](https://github.com/pouchdb/pouchdb-upsert#readme).
+
+### Eventual conflicts
+
+Now, let's move on to the second type: **eventual conflicts**.
+
+Imagine two PouchDB databases have both gone offline. The two separate users each make modifications to the same document, and then they come back online at a later time.
+
+Both users committed changes to the same version of the document, and their local databases did not throw 409 errors. What happens then?
+
+This is the classic "conflict" scenario, and CouchDB handles it very elegantly. By default, CouchDB will choose an arbitrary winner based on a deterministic algorithm, which means both users will see the same winner once they're back online. However, since the replication history is stored, you can always go back in time to resolve the conflict.
+
+To detect if a document is in conflict, you use the `{conflicts: true}` option when you `get()` it.
+
+```js
+db.get('docid', {conflicts: true}).then(function (doc) {
+ // do something with the doc
+}).catch(function (err) {
+ // handle any errors
+});
+```
+
+If the document has conflicts, then the `doc` will be returned with a `_conflicts` attribute, which may contain the revision IDs of conflicting revisions.
+
+For instance, imagine the `doc` returned is the following:
+
+```js
+{
+ "_id": "docid",
+ "_rev": "2-x",
+ "_conflicts": ["2-y"]
+}
+```
+
+Here we have two separate revisions (`2-x` and `2-y`) written by two separate databases, and one database's revision (`2-x`) has arbitrarily won.
+
+{% include alert/start.html variant="warning" %}
+{% markdown %}
+
+Normally, `_rev`s look more like `2-c1592ce7b31cc26e91d2f2029c57e621`, i.e. a digit followed by a very long hash. In these examples, `x` and `y` are used in place of the hash, for simplicity's sake.
+
+{% endmarkdown %}
+{% include alert/end.html %}
+
+Notice that the document's current revision starts with `2-`, and the conflicting version also starts with `2-`, indicating that they're both at the same level of the revision tree. (Revision hashes start with `1-`, `2-`, `3-`, etc., which indicates their distance from the first, "root" revision. The root always starts with `1-`.)
+
+Both databases will see the same conflict, assuming replication has completed. In fact, all databases in the network will see the exact same revision history – much like Git.
+
+To fetch the losing revision, you simply `get()` it using the `rev` option:
+
+```js
+db.get('docid', {rev: '2-y'}).then(function (doc) {
+ // do something with the doc
+}).catch(function (err) {
+ // handle any errors
+});
+```
+
+At this point, you can present both versions to the user, or resolve the conflict automatically using your preferred conflict resolution strategy: last write wins, first write wins, [RCS](https://www.gnu.org/software/rcs/), etc.
+
+To mark a conflict as resolved, all you need to do is `remove()` the unwanted revisions. So for instance, to remove `'2-y'`, you would do:
+
+```js
+db.remove('docid', '2-y').then(function (doc) {
+ // yay, we're done
+}).catch(function (err) {
+ // handle any errors
+});
+```
+
+If you want to resolve the conflict by creating a new revision, you simply `put()` a new document on top of the current winner, and make sure that the losing revision is deleted.
+
+{% include anchor.html title="Accountants don't use erasers" hash="accountants-dont-use-erasers" %}
+
+Another conflict resolution strategy is to design your database so that conflicts are impossible. In practice, this means that you never update or remove existing documents – you only create new documents.
+
+This strategy has been called the "every doc is a delta" strategy. A classic use-case for this would be a checkbook app, where every document is simply an operation that increases or decreases the account balance:
+
+```js
+{_id: new Date().toJSON(), change: 100} // balance increased by $100
+{_id: new Date().toJSON(), change: -50} // balance decreased by $50
+{_id: new Date().toJSON(), change: 200} // balance increased by $200
+```
+
+In this system, it is impossible for two documents to conflict, because the document `_id`s are just timestamps. Ledger transactions are recorded in the order they were made, and at the end of the day, you only need to do an `allDocs()` or `query()` operation to sum the result.
+
+The wisdom of this strategy can be expressed by the maxim: ["Accountants don't use erasers"](https://queue.acm.org/detail.cfm?id=2884038). Like a diligent accountant, your app can just add new documents when you want to make a change, rather than going back and scrubbing out previous changes.
+
+There is also a PouchDB plugin that implements this strategy: [delta-pouch](https://github.com/redgeoff/delta-pouch).
+
+{% include anchor.html title="Next" hash="next" %}
+
+Now that we've settled our conflicts, let's take a look at the changes feed.
diff --git a/docs/version/9.0.0/guides/databases.md b/docs/version/9.0.0/guides/databases.md
new file mode 100644
index 0000000000..6e1052799c
--- /dev/null
+++ b/docs/version/9.0.0/guides/databases.md
@@ -0,0 +1,146 @@
+---
+index: 4
+layout: guide.html
+title: Working with databases
+sidebar: guides_nav.html
+---
+
+PouchDB databases come in two flavors: local and remote.
+
+{% include anchor.html title="Local databases" hash="local–databases" %}
+
+To create a local database, you simply call `new PouchDB` and give it a name:
+
+```js
+const db = new PouchDB('kittens');
+```
+
+You can see a **[live example](http://bl.ocks.org/nolanlawson/bddac54b92c2d8d39241)** of this code.
+
+{% include alert/start.html variant="info" %}
+
+Protip: whenever you see a live example in this guide, you can download it to follow along at home! For example, to run this example, just enter the following commands in your command prompt:
+
+
+ git clone https://gist.github.com/bddac54b92c2d8d39241.git kittens
+ cd kittens
+ python -m SimpleHTTPServer # for Python 2
+ python -m http.server # for Python 3
+
+
+Now the site is up and running at http://localhost:8000. To find the correct gist.github.com URL, just click the "block" number at the top of the page.
+
+{% include alert/end.html %}
+
+{% include anchor.html title="Remote databases" hash="remote-databases" %}
+
+To create a remote database, you call `new PouchDB` and give it a path to a database in CouchDB.
+
+```js
+const db = new PouchDB('http://localhost:5984/kittens');
+```
+
+{% include alert/start.html variant="info" %}
+note: The remote database will not be created until you do an API call, e.g.: db.info(). The reason behind that is that the PouchDB constructor is completely
+synchronous, for ease of error handling (i.e. no asynchronous errors).
+{% include alert/end.html %}
+
+The structure of a CouchDB URL is very simple:
+
+```
+http:// localhost:5984 /kittens
+⌞_____⌟ ⌞____________⌟ ⌞_____⌟
+ | | |
+Protocol Where CouchDB database
+(https if itself is name
+Cloudant) hosted
+
+```
+
+If the remote database doesn't exist, then PouchDB will create it for you.
+
+You can verify that your database is working by visiting the URL [http://localhost:5984/kittens](http://localhost:5984/kittens). You should see something like this:
+
+```js
+{"db_name":"kittens","doc_count":0,"doc_del_count":0,"update_seq":0,"purge_seq":0,"compact_running":false,"disk_size":79,"data_size":0,"instance_start_time":"1410722558431975","disk_format_version":6,"committed_update_seq":0}
+```
+
+If instead you see:
+
+```js
+{"error":"not_found","reason":"no_db_file"}
+```
+
+Then check to make sure that your remote CouchDB has started up correctly. Common errors (such as CORS) are [listed here](/errors.html).
+
+
+{% include anchor.html title="Get basic info about the database" hash="get-basic-info-about-the–database" %}
+
+You can see basic information about the database by using the `info()` method.
+
+```js
+db.info().then(function (info) {
+ console.log(info);
+})
+```
+
+The local database should show something like:
+
+```js
+{"doc_count":0,"update_seq":0,"db_name":"kittens"}
+```
+
+The remote database may have a bit more information:
+
+```js
+{"db_name":"kittens","doc_count":0,"doc_del_count":0,"update_seq":0,"purge_seq":0,"compact_running":false,"disk_size":79,"data_size":0,"instance_start_time":"1410722558431975","disk_format_version":6,"committed_update_seq":0}
+```
+
+The most important bits of information are:
+
+* `doc_count`: the number of undeleted documents in the database
+* `db_name`: the name of the database
+
+
+{% include anchor.html title="Debugging" hash="debugging" %}
+
+### IndexedDB/WebSQL inspectors
+
+You can use the normal developer tools to see what your database looks like under the hood.
+
+In Chrome, just choose *Overflow icon* ☰ → *Tools* → *Developer Tools*. Then click the *Resources* tab, then *IndexedDB*, and you should see the following:
+
+{% include img.html src="dev_tools.png" alt="Chrome Developer Tools" %}
+
+This is the raw IndexedDB representation of your PouchDB, so it is very fine-grained. However, you may find it useful.
+
+In Safari, your database will be under *Develop* → *Show Web Inspector* → *Resources* → *Databases*.
+
+{% include img.html src="safari_inspector.png" alt="Web Inspector in Safari" %}
+
+{% include anchor.html title="Deleting your local database" hash="deleting-your-local-database" %}
+
+During development, it's often useful to destroy the local database, so you can see what your users will experience when they visit your site for the first time. A page refresh is not enough, because the data will still be there!
+
+In Chrome, you can use the [Clear Cache extension](https://chrome.google.com/webstore/detail/clear-cache/cppjkneekbjaeellbfkmgnhonkkjfpdn), which will add a trashcan icon to your toolbar, which you can click to delete all local data (IndexedDB, WebSQL, LocalStorage, cookies, etc.).
+
+In Firefox, you can use the [Clear Browsing Data add-on](https://addons.mozilla.org/en-US/firefox/addon/clear-browsing-data/), which adds a toolbar button to delete your IndexedDB with one click.
+
+In Safari, you can simply click *Safari* → *Clear History and Website Data*.
+
+{% include anchor.html title="Differences between the local and remote databases" hash="differences-between-the-local-and-remote-databases" %}
+
+When you create a local PouchDB database, it uses whatever underlying datastore is available - IndexedDB in most browsers, WebSQL in older browsers, and LevelDB in Node.js.
+
+When you create a remote PouchDB database, it communicates directly with the remote database – CouchDB, Cloudant, Couchbase, etc.
+
+The goal of PouchDB is to allow you to seamlessly communicate with one or the other. You should not notice many differences between the two, except that of course the local one is much faster!
+
+{% include anchor.html title="Related API documentation" hash="related-api-documentation" %}
+
+* [new PouchDB() (constructor)](/api.html#create_database)
+* [Debug mode](/api.html#debug_mode)
+
+{% include anchor.html title="Next" hash="next" %}
+
+Now that you've created some databases, let's put some documents in 'em!
diff --git a/docs/version/9.0.0/guides/documents.md b/docs/version/9.0.0/guides/documents.md
new file mode 100644
index 0000000000..4b3676e775
--- /dev/null
+++ b/docs/version/9.0.0/guides/documents.md
@@ -0,0 +1,191 @@
+---
+index: 5
+layout: guide.html
+title: Working with documents
+sidebar: guides_nav.html
+---
+
+{% include anchor.html title="What's a document?" hash="whats-a–document" %}
+
+PouchDB is a NoSQL database, meaning that you store unstructured *documents* rather than explicitly specifying a schema with rows, tables, and all that jazz.
+
+A document might look like this:
+
+```js
+{
+ "_id": "mittens",
+ "name": "Mittens",
+ "occupation": "kitten",
+ "age": 3,
+ "hobbies": [
+ "playing with balls of yarn",
+ "chasing laser pointers",
+ "lookin' hella cute"
+ ]
+}
+```
+
+If you come from a SQL background, this handy conversion chart may help:
+
+
+
+
+
SQL concept
+
PouchDB concept
+
+
+
table
+
no equivalent
+
+
+
row
+
document
+
+
+
+
column
+
field
+
+
+
primary key
+
primary key (_id)
+
+
+
index
+
view
+
+
+
+
+We'll discuss these concepts later on.
+
+{% include anchor.html title="Storing a document" hash="storing-a–document" %}
+
+To store a document, you simply `put` it:
+
+```js
+const doc = {
+ "_id": "mittens",
+ "name": "Mittens",
+ "occupation": "kitten",
+ "age": 3,
+ "hobbies": [
+ "playing with balls of yarn",
+ "chasing laser pointers",
+ "lookin' hella cute"
+ ]
+};
+db.put(doc);
+```
+
+Whenever you `put()` a document, it must have an `_id` field so that you can retrieve it later.
+
+So now let's `get()` the document by using its `_id`:
+
+```js
+db.get('mittens').then(function (doc) {
+ console.log(doc);
+});
+```
+
+You should see:
+
+```js
+{
+ "name": "Mittens",
+ "occupation": "kitten",
+ "age": 3,
+ "hobbies": [
+ "playing with balls of yarn",
+ "chasing laser pointers",
+ "lookin' hella cute"
+ ],
+ "_id": "mittens",
+ "_rev": "1-bea5fa18e06522d12026f4aee6b15ee4"
+}
+```
+
+You can see a **[live example](http://bl.ocks.org/nolanlawson/c02bba75247012afb1bf)** of this code.
+
+The document looks exactly the same as when we put it, except... aha! What is this? There is a new field, `_rev`, that contains what looks like garbage. PouchDB gots some 'splainin' to do.
+
+{% include anchor.html title="Understanding revisions (`_rev`)" hash="understanding-revisions-rev" %}
+
+The new field, `_rev` is the *revision marker*. It is a randomly-generated ID that changes whenever a document is created or updated.
+
+Unlike most other databases, whenever you update a document in PouchDB or CouchDB, you must present the *entire document* along with its current *revision marker*.
+
+For instance, to increment Mittens' age to 4, we would do:
+
+```js
+doc.age = 4;
+doc._rev = "1-bea5fa18e06522d12026f4aee6b15ee4";
+db.put(doc);
+```
+
+If you fail to include the correct `_rev`, you will get the following sad error:
+
+```js
+{
+ "status": 409,
+ "name": "conflict",
+ "message": "Document update conflict"
+}
+```
+
+`HTTP 409` is a standard HTTP error message that indicates a conflict.
+
+{% include anchor.html title="Updating documents correctly" hash="updating-documents–correctly" %}
+
+So to update Mittens' age, we will first need to fetch Mittens from the database, to ensure that we have the correct `_rev` before we put them back. We don't need to manually assign the `_rev` value here (like we did above), as it is already in the `doc` we're fetching.
+
+```js
+// fetch mittens
+db.get('mittens').then(function (doc) {
+ // update their age
+ doc.age = 4;
+ // put them back
+ return db.put(doc);
+}).then(function () {
+ // fetch mittens again
+ return db.get('mittens');
+}).then(function (doc) {
+ console.log(doc);
+});
+```
+
+You can see a **[live example](http://bl.ocks.org/nolanlawson/d6daa02ca3875d1222dd)** of this code.
+
+{% include alert/start.html variant="info" %}
+
+Don't worry if the structure of this code seems strange! It's using promises, which will be discussed in the next chapter.
+
+{% include alert/end.html %}
+
+Now you should see the following:
+
+```js
+{
+ "name": "Mittens",
+ "occupation": "kitten",
+ "age": 4,
+ "hobbies": [
+ "playing with balls of yarn",
+ "chasing laser pointers",
+ "lookin' hella cute"
+ ],
+ "_id": "mittens",
+ "_rev": "2-3e3fd988b331193beeeea2d4221b57e7"
+}
+```
+
+As you can see, we have successfully updated Mittens' age to 4 (they grow up so fast!), and their revision marker has also changed to `"2-3e3fd988b331193beeeea2d4221b57e7"`. If we wanted to increment their age to 5, we would need to supply this new revision marker.
+
+{% include anchor.html title="Related API documentation" hash="related-api-documentation" %}
+
+* [get()](/api.html#fetch_document)
+* [put()](/api.html#create_document)
+
+{% include anchor.html title="Next" hash="next" %}
+
+Now that you understand a bit about how to create and update documents, let's take a small detour to talk about asynchronous code.
diff --git a/docs/version/9.0.0/guides/guides.json b/docs/version/9.0.0/guides/guides.json
new file mode 100644
index 0000000000..cd20b85a28
--- /dev/null
+++ b/docs/version/9.0.0/guides/guides.json
@@ -0,0 +1,3 @@
+{
+ "tags": "guides"
+}
diff --git a/docs/version/9.0.0/guides/index.md b/docs/version/9.0.0/guides/index.md
new file mode 100644
index 0000000000..b22939bcdb
--- /dev/null
+++ b/docs/version/9.0.0/guides/index.md
@@ -0,0 +1,56 @@
+---
+index: 1
+layout: guide.html
+nav: Intro
+title: Introduction to PouchDB
+sidebar: guides_nav.html
+---
+
+Welcome to the PouchDB guide! Consider this your starting point for anything and everything related to the world of PouchDB and CouchDB.
+
+For a quicker TodoMVC-based tutorial, you can also check out the ["Getting Started" guide]({{ site.baseurl }}/getting-started.html).
+
+Feel free to skip ahead using the sidebar at any time.
+
+{% include anchor.html title="What is PouchDB?" hash="what-is-pouchdb" %}
+
+**PouchDB** is a JavaScript implementation of [CouchDB](https://couchdb.apache.org). Its goal is to emulate the CouchDB API with near-perfect fidelity, while running in the browser or in Node.js.
+
+{% include anchor.html title="What is CouchDB?" hash="what-is-couchdb" %}
+
+**CouchDB** is a NoSQL database created in 2005 by Damien Katz, and now maintained by the Apache Software Foundation. If you are a JavaScript developer, you probably use CouchDB every day, because it's the core technology that powers [npm](https://www.npmjs.org/).
+
+{% include anchor.html title="Couchbase, CouchDB, Couch-what?" hash="couchbase-couchdb-couch-what" %}
+
+Today there are two major database companies that
+can trace their lineage back to CouchDB: [**Couchbase**](http://couchbase.com) and [**Cloudant**](http://cloudant.com). Both of them are separate products compared to CouchDB.
+
+However, all three of these databases share the same **CouchDB sync protocol**. This means that PouchDB can sync with either one of them, and you can always swap out one database for another. You're never locked in.
+
+In a sense, these databases are like competing phone companies, and the CouchDB sync protocol is the underlying telephony infrastructure.
+
+{% include anchor.html title="CouchDB's one-two punch: HTTP and sync" hash="http-and-sync" %}
+
+With so many SQL and NoSQL databases out there – MongoDB, PostgreSQL, MySQL, etc. – you may wonder why we chose to implement CouchDB instead of the others.
+
+We have two very good answers to that question: **HTTP** and **sync**.
+
+{% include anchor.html title="HTTP: the little protocol that could" hash="http" %}
+
+When working with databases, we're often accustomed to writing some kind of conversion layer between the database and our client-side applications. This means, however, that we are just translating database queries into RESTful HTTP calls, over and over. For every app we write.
+
+CouchDB throws this out the window by daring us to talk to the database directly, from our client-side apps. And it does so by using HTTP as its primary means of communication. No special protocol, no special drivers: just REST and HTTP. You can communicate with CouchDB entirely through your browser, `curl`, or a REST client like [Postman](https://chrome.google.com/webstore/detail/postman-rest-client/fdmmgilgnpjigdojojpjoooidkmcomcm).
+
+In this way, CouchDB truly is a "database for the web."
+
+{% include anchor.html title="Sync: CouchDB's killer feature" hash="sync" %}
+
+Another unique feature of CouchDB is that it was designed from the bottom-up to enable easy synchronization between different databases.
+
+For example, if you are worried about latency in your client-side applications, you can simply set up one CouchDB in Europe, another in North America, and another in Asia. After enabling continuous two-way replication between these databases, your clients can simply talk to whichever one is closer.
+
+PouchDB takes this one step further by putting the database inside your browser.
+
+{% include anchor.html title="Next" hash="next" %}
+
+Now that you understand the basics of the PouchDB/CouchDB universe, let's set up CouchDB!
diff --git a/docs/version/9.0.0/guides/local-documents.md b/docs/version/9.0.0/guides/local-documents.md
new file mode 100644
index 0000000000..e54a6c9158
--- /dev/null
+++ b/docs/version/9.0.0/guides/local-documents.md
@@ -0,0 +1,44 @@
+---
+index: 16
+layout: guide.html
+title: Local documents
+sidebar: guides_nav.html
+---
+
+"Local" documents are a special class of documents in PouchDB and CouchDB, which are used for storing local metadata about a database. You might never need them in your own app, but sometimes they can come in handy for advanced use cases.
+
+{% include anchor.html title="Local docs in a nutshell" hash="local-docs-in-a-nutshell" %}
+
+Local docs have the following characteristics:
+
+* They don't replicate.
+* They can't contain attachments.
+* They don't appear in `allDocs()`, `changes()`, or `query()`.
+* However, you can modify them with `put()`/`remove()`/`bulkDocs()`, and you can fetch them with `get()`.
+
+So basically, local docs only exist *for that database*, and they don't mix with the "normal" documents.
+
+To create a local doc, you simply use `'_local/'` as the prefix of the `_id`. This is supported in both CouchDB and PouchDB:
+
+```js
+db.put({
+ _id: '_local/foobar',
+ someText: 'yo, this is my local doc!'
+}).then(function () {
+ return db.get('_local/foobar');
+});
+```
+
+{% include anchor.html title="Advantages of local docs" hash="advantages-of-local–docs" %}
+
+Local docs are useful for small bits of configuration or metadata, which you don't necessarily want to replicate, but which you want to keep in the database anyway. Many PouchDB plugins and core components use local docs. For instance, the replication algorithm uses them to store checkpoints, and map/reduce uses them to keep track of what's been `emit`ted.
+
+Local docs also have some good performance characteristics compared to regular docs. They don't have a version history, so only the most recent revision is ever stored in the database. This means that `put()`s and `get()`s are faster for local docs than for regular docs, and that local docs tend to take up less space on disk. In a sense, they are auto-compacted, although they take up even less space on disk than documents in a compacted database.
+
+Regardless, you need to provide the current `_rev` when you update local docs, just like with regular docs.
+
+{% include anchor.html title="Related API documentation" hash="related-api-documentation" %}
+
+* [put()](/api.html#create_document)
+* [get()](/api.html#fetch_document)
+* [remove()](/api.html#delete_document)
diff --git a/docs/version/9.0.0/guides/mango-queries.md b/docs/version/9.0.0/guides/mango-queries.md
new file mode 100644
index 0000000000..64ad8752e4
--- /dev/null
+++ b/docs/version/9.0.0/guides/mango-queries.md
@@ -0,0 +1,364 @@
+---
+index: 13
+layout: guide.html
+title: Mango queries
+sidebar: guides_nav.html
+---
+
+Mango queries, also known as `pouchdb-find` or the `find()` API, are a structured query API that allows you to build _secondary indexes_ beyond the built-in `allDocs()` and `changes()` indexes.
+
+This API is useful for answering questions like:
+
+- find all documents where the `type` is `'user'`
+- find all users whose `age` is greater than `21`
+- find all Pokémon whose `name` starts with `'pika'`
+- etc.
+
+{% include anchor.html title="Installation" hash="installation" %}
+
+The `find()` API is currently offered as a separate plugin, meaning that you must install it on top of `pouchdb.js`. Here's how to do so:
+
+### Script tags
+
+```html
+
+
+```
+
+The `pouchdb.find.js` file is available in the `pouchdb` package in npm/Bower, on [unpkg](https://unpkg.com/pouchdb/dist/), or [as a GitHub download](https://github.com/apache/pouchdb/releases/download/{{ site.version }}/pouchdb.find.js). Note it must be placed after `pouchdb.js`.
+
+### npm
+
+If you are using Node, Browserify, Webpack, Rollup, etc., then you can install it like so:
+
+```bash
+npm install --save pouchdb-find
+```
+
+Then in code:
+
+```js
+const PouchDB = require('pouchdb');
+PouchDB.plugin(require('pouchdb-find'));
+```
+
+{% include anchor.html title="Query language" hash="query-language" %}
+
+The [Mango query language](https://github.com/cloudant/mango) is a DSL inspired by MongoDB, which allows you to define an index that is then used for querying. One quick way to understand how this works is to use the [live query demo](https://nolanlawson.github.io/pouchdb-find/).
+
+At a basic level, there are two steps to running a query: `createIndex()` (to define which fields to index) and `find()` (to query the index).
+
+For instance, let's imagine a simple index to look up all documents whose `name` is `"mario"`. First we'll create it:
+
+```js
+db.createIndex({
+ index: {fields: ['name']}
+});
+```
+
+This returns a Promise that resolves once the index is created. At this point, we have an index based on the `"name"` field, so we can use it for lookup:
+
+```js
+db.find({
+ selector: {
+ name: 'mario'
+ }
+});
+```
+
+This returns a Promise containing an array of all documents that match this selector. Note that this is equivalent to using the `$eq` (equals) operator:
+
+```js
+db.find({
+ selector: {
+ name: {$eq: 'mario'}
+ }
+});
+```
+
+The important thing to understand is that, for a typical database, `createIndex()` is the expensive operation, because it is looping through all documents in the database and building a [B-tree](https://en.wikipedia.org/wiki/B-tree) based on the `name` value.
+
+Once the B-tree is built up, though, the `find()` is relatively cheap. (If this were _not_ the case, then we would be better off just using `allDocs()` to iterate through the database ourselves!)
+
+Once we have an index on `name`, we can also sort all documents by `name`:
+
+```js
+db.find({
+ selector: {
+ name: {$gte: null}
+ },
+ sort: ['name']
+});
+```
+
+Note that we are specifying that the `name` must be greater than or equal to `null`, which is a workaround for the fact that the Mango query language requires us to have a selector. In [CouchDB collation order](https://docs.couchdb.org/en/stable/ddocs/views/collation.html), `null` is the "lowest" value, and so this will return all documents regardless of their `name` value.
+
+{% include anchor.html title="Pagination" hash="pagination" %}
+
+Reading all documents in the database and sorting them by a particular value is neat, but we could do this ourselves with `allDocs()`, and it would have the same performance impact. Where it gets more interesting is when we use `limit`:
+
+```js
+db.find({
+ selector: {
+ name: {$gte: null}
+ },
+ sort: ['name'],
+ limit: 10
+});
+```
+
+In this case, we only get 10 documents back, but they are the first 10 documents, sorted by name. This means that we have only read 10 documents out of the database into memory, which can be used for [efficient pagination]({{ site.baseurl }}/2014/04/14/pagination-strategies-with-pouchdb.html).
+
+For instance, if we are displaying the first 10 results on a single page, and the user clicks "next" to see the next page, we can restructure our query based on the last result, to continue the pagination. Let's imagine the first 10 documents' `name`s are:
+
+```js
+[
+ 'abby', 'bertrand', 'clarice', 'don', 'emily',
+ 'fumiko', 'gunther', 'horatio', 'ike', 'joy'
+]
+```
+
+For our next 10 pages of results, the query becomes:
+
+```js
+db.find({
+ selector: {
+ name: {$gt: 'joy'}
+ },
+ sort: ['name'],
+ limit: 10
+});
+```
+
+Because we are now specifying that the `name` must be greater than `'joy'`, we are guaranteed to get the next-highest result after `'joy'`, which may (for instance) look like this:
+
+```js
+[
+ 'kim', 'lin', 'maria', 'nell', 'oliver',
+ 'pat', 'quincy', 'roy', 'sam', 'tanya'
+]
+```
+
+In this way, we can continue paginating by using the last value as our next starting point. At any given point in time, there are only 10 documents stored in memory at once, which is great for performance.
+
+{% include anchor.html title="Indexing on more than one field" hash="more-than-one-field" %}
+
+Sometimes an index is not as simple as "find all documents whose `name` is `"mario"`. Sometimes you want to do something fancy, such as "find all documents whose `name` is `"mario"` and whose `age` is greater than `21`". In those cases, you can index on more than one field:
+
+```js
+db.createIndex({
+ index: {
+ fields: ['name', 'age']
+ }
+}).then(function () {
+ return db.find({
+ selector: {
+ name: 'mario',
+ age: {$gt: 21}
+ }
+ });
+});
+```
+
+One thing to note is that the order of these fields matters when creating your index. For instance, the following would _not_ work:
+
+```js
+/* THIS WON'T WORK! */
+db.createIndex({
+ index: {
+ fields: ['age', 'name']
+ }
+}).then(function () {
+ return db.find({
+ selector: {
+ name: 'mario',
+ age: {$gt: 21}
+ }
+ });
+});
+```
+
+The reason for this is easy to understand if we imagine how this index would sort a hypothetical database:
+
+
+
+
+
name
+
age
+
+
+
Luigi
+
17
+
+
+
Luigi
+
28
+
+
+
Mario
+
18
+
+
+
Mario
+
21
+
+
+
Mario
+
22
+
+
+
Mario
+
26
+
+
+
Peach
+
17
+
+
+
Peach
+
21
+
+
+
Peach
+
25
+
+
+
+
+In the above table, the documents are sorted by `['name', 'age']`, and our "Marios above the age of 21" are very clearly grouped together.
+
+However, if we were to change the order, and sort them by `['age', 'name']`, it would look instead like this:
+
+
+
+
+
age
+
name
+
+
+
17
+
Luigi
+
+
+
17
+
Peach
+
+
+
18
+
Mario
+
+
+
21
+
Mario
+
+
+
21
+
Peach
+
+
+
22
+
Mario
+
+
+
25
+
Peach
+
+
+
26
+
Mario
+
+
+
28
+
Luigi
+
+
+
+
+If we imagine our `find()` query as a "slice" of the data, it's obvious that there's no slice that corresponds to "all Marios whose age is greater than 21." Instead, our documents are sorted by `age`, and then documents with the same `age` are sorted by `name`.
+
+This index may be good for answering questions like "find all 17-year-olds whose name starts with letters N-Z", but it's not very good for answering questions like "find all people with a certain name, older than a certain age."
+
+This shows that it's important to carefully design an index before creating a query to use that index. Otherwise, the query planner may fall back to in-memory querying, which can be expensive.
+
+{% include anchor.html title="Performance notes" hash="performance-notes" %}
+
+The Mango query language is generally very permissive, and allows you to write queries that may not perform very well, but will run regardless. For instance, you may create an index with `createIndex()`, but then write a `find()` query that doesn't actually use that index. In general, the query planner tries to find the most appropriate index, but it may fall back to in-memory querying.
+
+As a straightforward example, if you query using the `_id` field, then the query planner will automatically map that directly to an `allDocs()` query. However, if you query for a field that isn't yet indexed, then it will simply use `allDocs()` to read in all documents from the database (!) and then filter in-memory. This can lead to poor performance, especially if your database is large.
+
+If you're ever wondering how the query planner is interpreting your query, you can use the explain endpoint:
+
+```js
+db.explain({
+ selector: {
+ name: 'mario',
+ age: {$gt: 21}
+ }
+})
+.then(function (explained) {
+ // detailed explained info can be viewed
+});
+
+```
+
+In the console, the query planner will show a detailed explanation of how it has interpreted the query, whether it uses any indexes, and whether any parts of the query need to be executed in-memory.
+
+
+You may also want to pay attention to the `"warning"` value included in your results set, indicating that there was no index that matched the given query. For instance, the warning may look like this:
+
+```js
+{
+ "docs": [ /* ... */ ],
+ "warning": "No matching index found, create an index to optimize query time."
+}
+```
+
+{% include anchor.html title="Set which index to use" hash="use_index" %}
+
+When creating a query, by settings the `use_index` field, it is possible to tell pouchdb-find which index to use.
+The below example shows how to do that.
+
+```js
+db.createIndex({
+ index: {
+ fields: ['age', 'name'],
+ ddoc: "my-index-design-doc"
+ }
+}).then(function () {
+ return db.find({
+ selector: {
+ name: 'mario',
+ age: {$gt: 21},
+ },
+ use_index: 'my-index-design-doc'
+ });
+});
+```
+
+{% include anchor.html title="Further reading" hash="further-reading" %}
+
+The Mango query language is quite large and supports many options. Some of the more common ones include:
+
+* `$eq`: equals
+* `$gt`: greater than
+* `$gte`: greater than or equal to
+* `$lt`: less than
+* `$lte`: less than or equal to
+
+There are many more options besides these, although note that not all of them can take advantage of indexes. For instance, `$regex`, `$ne`, and `$not` cannot use on-disk indexes, and must use in-memory filtering instead.
+
+The most complete documentation for selector options can be found in the [CouchDB `_find` documentation](https://docs.couchdb.org/en/stable/api/database/find.html). You might also look at the [Cloudant Query Language](https://docs.cloudant.com/cloudant_query.html) documentation (which is nearly identical to Mango, other than `text` and other Cloudant-specific features). PouchDB uses CouchDB as the reference implementation; they ought to be functionally identical.
+
+It should be noted that, over HTTP, this API currently works with CouchDB 2.0+, Cloudant, and PouchDB Server.
+CouchDB 2.0 is the reference implementation, so the API should be the same. CouchDB 1.6.1 and below is not supported.
+
+{% include anchor.html title="Related API documentation" hash="related-api-documentation" %}
+
+* [createIndex()](/api.html#create_index)
+* [find()](/api.html#query_index)
+* [getIndexes()](/api.html#list_indexes)
+* [deleteIndex()](/api.html#delete_index)
+
+{% include anchor.html title="Next" hash="next" %}
+
+Now that we've learned how to do structured Mango queries, let's try some more advanced queries, using _map/reduce_.
diff --git a/docs/version/9.0.0/guides/queries.md b/docs/version/9.0.0/guides/queries.md
new file mode 100644
index 0000000000..46bcfb409a
--- /dev/null
+++ b/docs/version/9.0.0/guides/queries.md
@@ -0,0 +1,240 @@
+---
+index: 14
+layout: guide.html
+title: Map/reduce queries
+sidebar: guides_nav.html
+---
+
+Map/reduce queries, also known as the `query()` API, are one of the most powerful features in PouchDB. However, they can be quite tricky to use, and so this guide is designed to dispell some of the mysteries around them.
+
+The first thing to understand is that you don't need map/reduce queries if you merely want to look up documents by `_id` or sort them by `_id`. The `allDocs()` API already does this, using an efficient built-in index (see ["bulk operations"](bulk-operations.html) for details).
+
+The second thing to know is that map/reduce is also unnecessary if you want to sort documents by their update time – this is exactly what the [changes feed](changes.html) does! Again, this is a built-in index that you get for free.
+
+Finally, it's important to understand that [Mango queries](mango-queries.html) are much easier to use than map/reduce queries, and they can usually satisfy 99% of use cases. The point of map/reduce is to provide an _extremely advanced_ API for building secondary indexes, suitable for those with specific querying needs.
+
+So now that you've read the fine print, let's talk about how map/reduce queries actually work!
+
+{% include anchor.html title="Mappin' and reducin'" hash="mappin-and-reducin" %}
+
+The PouchDB `query()` API (which corresponds to the `_view` API in CouchDB) has two modes: temporary queries and persistent queries.
+
+### Temporary queries
+
+**Temporary queries** are very slow, and we only recommend them for quick debugging during development. To use a temporary query, you simply pass in a `map` function:
+
+```js
+db.query(function (doc, emit) {
+ emit(doc.name);
+}, {key: 'foo'}).then(function (result) {
+ // found docs with name === 'foo'
+}).catch(function (err) {
+ // handle any errors
+});
+```
+
+In the above example, the `result` object will contain stubs of documents where the `name` attribute is equal to `'foo'`. To include the document in each row of results, use the `include_docs` option.
+
+{% include alert/start.html variant="info" %}
+
+The emit pattern is part of the standard CouchDB map/reduce API. What the function basically says is, "for each document, emit doc.name as a key."
+
+{% include alert/end.html %}
+
+### Persistent queries
+
+**Persistent queries** are much faster, and are the intended way to use the `query()` API in your production apps. To use persistent queries, there are two steps.
+
+First, you create a **design document**, which describes the `map` function you would like to use:
+
+```js
+// document that tells PouchDB/CouchDB
+// to build up an index on doc.name
+const ddoc = {
+ _id: '_design/my_index',
+ views: {
+ by_name: {
+ map: function (doc) { emit(doc.name); }.toString()
+ }
+ }
+};
+// save it
+pouch.put(ddoc).then(function () {
+ // success!
+}).catch(function (err) {
+ // some error (maybe a 409, because it already exists?)
+});
+```
+
+{% include alert/start.html variant="info" %}
+
+The .toString() at the end of the map function is necessary to prep the
+object for becoming valid JSON.
+
+{% include alert/end.html %}
+
+{% include alert/start.html variant="info" %}
+
+The emit function will be available in scope when the map function is
+run, so don't pass it in as a parameter.
+
+{% include alert/end.html %}
+
+Then you actually query it, by using the name you gave the design document when you saved it:
+
+```js
+db.query('my_index/by_name').then(function (res) {
+ // got the query results
+}).catch(function (err) {
+ // some error
+});
+```
+
+Note that, the first time you query, it will be quite slow because the index isn't
+built until you query it. To get around this, you can do an empty query to kick
+off a new build:
+
+```js
+db.query('my_index/by_name', {
+ limit: 0 // don't return any results
+}).then(function (res) {
+ // index was built!
+}).catch(function (err) {
+ // some error
+});
+```
+
+After this, your queries will be much faster.
+
+{% include alert/start.html variant="info"%}
+
+CouchDB builds indexes in exactly the same way as PouchDB. So you may want to familiarize yourself with the "stale" option in order to get the best possible performance for your app.
+
+{% include alert/end.html %}
+
+
+{% include anchor.html title="More about map/reduce" hash="more-about-map-reduce" %}
+
+That was a fairly whirlwind tour of the `query()` API, so let's get into more detail about how to write your map/reduce functions.
+
+#### Indexes in SQL databases
+
+Quick refresher on how indexes work: in relational databases like MySQL and PostgreSQL, you can usually query whatever field you want:
+
+```sql
+SELECT * FROM pokemon WHERE name = 'Pikachu';
+```
+
+But if you don't want your performance to be terrible, you first add an index:
+
+```sql
+ALTER TABLE pokemon ADD INDEX myIndex ON (name);
+```
+
+The job of the index is to ensure the field is stored in a B-tree within the database, so your queries run in _O(log(n))_ time instead of _O(n)_ time.
+
+#### Indexes in NoSQL databases
+
+All of the above is also true in document stores like CouchDB and MongoDB, but conceptually it's a little different. By default, documents are assumed to be schemaless blobs with one primary key (called `_id` in both Mongo and Couch), and any other keys need to be specified separately. The concepts are largely the same; it's mostly just the vocabulary that's different.
+
+In CouchDB, queries are called _map/reduce functions_. This is because, like most NoSQL databases, CouchDB is designed to scale well across multiple computers, and to perform efficient query operations in parallel. Basically, the idea is that you divide your query into a _map_ function and a _reduce_ function, each of which may be executed in parallel in a multi-node cluster.
+
+#### Map functions
+
+It may sound daunting at first, but in the simplest (and most common) case, you only need the _map_ function. A basic map function might look like this:
+
+```js
+function myMapFunction(doc) {
+ emit(doc.name);
+}
+```
+
+This is functionally equivalent to the SQL index given above. What it essentially says is: "for each document in the database, emit its name as a key."
+
+And since it's just JavaScript, you're allowed to get as fancy as you want here:
+
+```js
+function myMapFunction(doc) {
+ if (doc.type === 'pokemon') {
+ if (doc.name === 'Pikachu') {
+ emit('Pika pi!');
+ } else {
+ emit(doc.name);
+ }
+ }
+}
+```
+
+Then you can query it:
+
+```js
+// find pokemon with name === 'Pika pi!'
+pouch.query(myMapFunction, {
+ key : 'Pika pi!',
+ include_docs : true
+}).then(function (result) {
+ // handle result
+}).catch(function (err) {
+ // handle errors
+});
+
+// find the first 5 pokemon whose name starts with 'P'
+pouch.query(myMapFunction, {
+ startkey : 'P',
+ endkey : 'P\ufff0',
+ limit : 5,
+ include_docs : true
+}).then(function (result) {
+ // handle result
+}).catch(function (err) {
+ // handle errors
+});
+```
+
+{% include alert/start.html variant="info"%}
+
+The pagination options for query() – i.e., startkey/endkey/key/keys/skip/limit/descending – are exactly the same as with allDocs(). For a guide to pagination, read the Bulk operations guide or Pagination strategies with PouchDB.
+
+{% include alert/end.html %}
+
+#### Reduce functions
+
+As for _reduce_ functions, there are a few handy built-ins that do aggregate operations (`'_sum'`, `'_count'`, and `'_stats'`), and you can typically steer clear of trying to write your own:
+
+```js
+// emit the first letter of each pokemon's name
+const myMapReduceFun = {
+ map: function (doc) {
+ emit(doc.name.charAt(0));
+ },
+ reduce: '_count'
+};
+// count the pokemon whose names start with 'P'
+pouch.query(myMapReduceFun, {
+ key: 'P', reduce: true, group: true
+}).then(function (result) {
+ // handle result
+}).catch(function (err) {
+ // handle errors
+});
+```
+
+If you're adventurous, though, you should check out the [CouchDB documentation](http://couchdb.readthedocs.org/en/latest/couchapp/views/intro.html) or the [PouchDB documentation]({{ site.baseurl }}/api.html#query_database) for details on reduce functions.
+
+{% include anchor.html title="Avoiding map/reduce" hash="avoiding-map-reduce" %}
+
+The map/reduce API is complex, and it can be computationally expensive because it requires building up an entirely new index. Therefore, it's good to know some tricks for avoiding the map/reduce API when you don't need it:
+
+1. If you can use `allDocs()` or `changes()` instead of the `query()` API, do it!
+2. If your query is simple enough that you can use `find()`, use that instead.
+3. Read the [12 tips for better code with PouchDB](/2014/06/17/12-pro-tips-for-better-code-with-pouchdb.html), especially the tip to "use and abuse your doc _ids."
+4. If your data is highly relational, try the [relational-pouch](https://github.com/nolanlawson/relational-pouch) plugin, which follows this advice, and only uses `_id` and `allDocs()` under the hood.
+
+{% include anchor.html title="Related API documentation" hash="related-api-documentation" %}
+
+* [query()](/api.html#query_database)
+* [viewCleanup()](/api.html#view_cleanup)
+
+{% include anchor.html title="Next" hash="next" %}
+
+Now that we've learned how to map reduce, map reuse, and map recycle, let's move on to `destroy()` and `compact()`.
diff --git a/docs/version/9.0.0/guides/replication.md b/docs/version/9.0.0/guides/replication.md
new file mode 100644
index 0000000000..4f6a724205
--- /dev/null
+++ b/docs/version/9.0.0/guides/replication.md
@@ -0,0 +1,194 @@
+---
+index: 10
+layout: guide.html
+title: Replication
+sidebar: guides_nav.html
+---
+
+PouchDB and CouchDB were designed for one main purpose: **sync**. Jason Smith has [a great quote](http://nodeup.com/thirtyseven) about this:
+
+> The way I like to think about CouchDB is this: CouchDB is bad at everything, *except syncing*. And it turns out that's the most important feature you could ever ask for, for many types of software."
+
+When you first start using CouchDB, you may become frustrated because it doesn't work quite like other databases. Unlike most databases, CouchDB requires you to manage revisions (`_rev`), which can be tedious.
+
+However, CouchDB was designed with sync in mind, and this is exactly what it excels at. Many of the rough edges of the API serve this larger purpose. For instance, managing your document revisions pays off in the future, when you eventually need to start dealing with conflicts.
+
+{% include anchor.html title="CouchDB sync" hash="couchdb-sync" %}
+
+CouchDB sync has a unique design. Rather than relying on a master/follower architecture, CouchDB
+supports a **multi-master** architecture. You can think of this as a system where any node can be written to or read from, and where you don't have to care which one is the "master" and which one is the "follower." In CouchDB's egalitarian world, every citizen is as worthy as another.
+
+
+ {% include img.html src="offline_replication.gif" alt="Offline replication with CouchDB." %}
+
+
(Thanks to IBM for the image: http://www.ibm.com/developerworks/library/wa-couchdb/)
+
+
+
+When you use PouchDB, CouchDB, and other members of the Couch family, you
+don't have to worry which database is the "single source of truth." They all are. According to the CAP theorem, a database can only have at most 2 of 3 properties: Consistency, Availability, or Partition-Tolerance. Typical relational databases such as MySQL are CP, which means they are consistent and tolerant to node partitions, at the expense of availability. CouchDB is an AP database, meaning that it's **P**artition-Tolerant,
+every node is **A**vailable at all times, but it's only eventually **C**onsistent.
+
+To illustrate, imagine a multi-node architecture with CouchDB servers spread across several continents. As long as you're willing to wait, the data will eventually flow
+from Australia to Europe to North America to wherever. Users around the world running PouchDB in their browsers or [Couchbase Lite](https://github.com/couchbase/couchbase-lite-ios)/[Cloudant Sync](https://github.com/cloudant/CDTDatastore) in their smartphones experience the
+same privileges. The data won't show up instantaneously, but depending on the Internet connection speed, it's usually close enough to real-time.
+
+In cases of conflict, CouchDB will choose an arbitrary winner that every node can agree upon deterministically. However, conflicts are still stored in the **revision tree** (similar to a Git history tree), which means that app developers can either surface the conflicts to the user, or just ignore them.
+
+In this way, CouchDB replication "just works."
+
+{% include anchor.html title="Setting up sync" hash="setting-up-sync" %}
+
+As you already know, you can create either local PouchDBs:
+
+```js
+const localDB = new PouchDB('mylocaldb')
+```
+
+or remote PouchDBs:
+
+```js
+const remoteDB = new PouchDB('http://localhost:5984/myremotedb')
+```
+
+This pattern comes in handy when you want to share data between the two.
+
+The simplest case is **unidirectional replication**, meaning you just want one database to mirror its changes to a second one. Writes to the second database, however, will not propagate back to the master database.
+
+To perform unidirectional replication, you simply do:
+
+```js
+localDB.replicate.to(remoteDB).on('complete', function () {
+ // yay, we're done!
+}).on('error', function (err) {
+ // boo, something went wrong!
+});
+```
+
+Congratulations, all changes from the `localDB` have been replicated to the `remoteDB`.
+
+However, what if you want **bidirectional replication**? You could do:
+
+```js
+localDB.replicate.to(remoteDB);
+localDB.replicate.from(remoteDB);
+```
+
+However, to make things easier for your poor tired fingers, PouchDB has a shortcut API:
+
+```js
+localDB.sync(remoteDB);
+```
+
+These two code blocks above are equivalent. And the `sync` API supports all the same events as the `replicate` API:
+
+```js
+localDB.sync(remoteDB).on('complete', function () {
+ // yay, we're in sync!
+}).on('error', function (err) {
+ // boo, we hit an error!
+});
+```
+
+{% include anchor.html title="Live replication" hash="live–replication" %}
+
+Live replication (or "continuous" replication) is a separate mode where changes are propagated between the two databases as the changes occur. In other words, normal replication happens once, whereas live replication happens in real time.
+
+To enable live replication, you simply specify `{live: true}`:
+
+```js
+localDB.sync(remoteDB, {
+ live: true
+}).on('change', function (change) {
+ // yo, something changed!
+}).on('error', function (err) {
+ // yo, we got an error! (maybe the user went offline?)
+});
+```
+
+However, there is one gotcha with live replication: what if the user goes offline? In those cases, an error will be thrown and replication will stop.
+
+You can allow PouchDB to automatically handle this error, and retry until the connection is re-established, by using the `retry` option:
+
+```js
+localDB.sync(remoteDB, {
+ live: true,
+ retry: true
+}).on('change', function (change) {
+ // yo, something changed!
+}).on('paused', function (info) {
+ // replication was paused, usually because of a lost connection
+}).on('active', function () {
+ // replication was resumed
+}).on('error', function (err) {
+ // totally unhandled error (shouldn't happen)
+});
+```
+
+This is ideal for scenarios where the user may be flitting in and out of connectivity, such as on mobile devices.
+
+{% include anchor.html title="Canceling replication" hash="canceling—replication" %}
+
+Sometimes, you may want to manually cancel replication – for instance, because the user logged out. You can do so by calling `cancel()` and then waiting for the `'complete'` event:
+
+```js
+const syncHandler = localDB.sync(remoteDB, {
+ live: true,
+ retry: true
+});
+
+syncHandler.on('complete', function (info) {
+ // replication was canceled!
+});
+
+syncHandler.cancel(); // <-- this cancels it
+```
+
+The `replicate` API also supports canceling:
+
+```js
+const replicationHandler = localDB.replicate.to(remoteDB, {
+ live: true,
+ retry: true
+});
+
+replicationHandler.on('complete', function (info) {
+ // replication was canceled!
+});
+
+replicationHandler.cancel(); // <-- this cancels it
+```
+
+{% include anchor.html title="Deleting replicated databases" hash="delete-during-replication" %}
+-----
+
+One thing to note about replication is that it tracks the data within a database, not the database itself. If you [`destroy()`](/api.html#delete_database) a database that is being replicated to, the next time the replication starts it will transfer all of the data again, recreating the database to the state it was before it was `destroyed`. If you want the data within the database to be deleted you will need to delete via [`remove()`](/api.html#delete_document) or [`bulkDocs()`](/api.html#batch_create). The [pouchdb-erase](https://github.com/marten-de-vries/pouchdb-erase) plugin can help you remove the entire contents of a database.
+
+{% include anchor.html title="Fancy replication" hash="fancy-replication" %}
+-----
+
+Any PouchDB object can replicate to any other PouchDB object. So for instance, you can replicate two remote databases, or two local databases. You can also replicate from multiple databases into a single one, or from a single database into many others.
+
+This can be very powerful, because it enables lots of fancy scenarios. For example:
+
+1. You have an [in-memory PouchDB]({{ site.baseurl }}/adapters.html#pouchdb_in_the_browser) that replicates with a local PouchDB, acting as a cache.
+2. You have many remote CouchDB databases that the user may access, and they are all replicated to the same local PouchDB.
+3. You have many local PouchDB databases, which are mirrored to a single remote CouchDB as a backup store.
+
+The only limits are your imagination and your disk space.
+
+{% include alert/start.html variant="warning" %}
+
+When you replicate between two remote databases, the changes flow through PouchDB. If this is not what you want, then you should POST directly to the CouchDB _replicate endpoint, as described in the CouchDB replication guide.
+
+{% include alert/end.html %}
+
+
+{% include anchor.html title="Related API documentation" hash="related-api-documentation" %}
+
+* [replication()](/api.html#replication)
+* [sync()](/api.html#sync)
+
+{% include anchor.html title="Next" hash="next" %}
+
+Now that we have a grasp on replication, let's talk about an inconvenient fact of life: conflicts.
diff --git a/docs/version/9.0.0/guides/setup-couchdb.md b/docs/version/9.0.0/guides/setup-couchdb.md
new file mode 100644
index 0000000000..19c962c299
--- /dev/null
+++ b/docs/version/9.0.0/guides/setup-couchdb.md
@@ -0,0 +1,103 @@
+---
+index: 2
+layout: guide.html
+title: Setting up CouchDB
+sidebar: guides_nav.html
+---
+
+{% include anchor.html title="CouchDB: PouchDB's older sibling" hash="couchdb-pouchdbs-older-sibling" %}
+
+One of the main benefits of learning PouchDB is that it's exactly the same as CouchDB. In fact, PouchDB is a shameless plagiarist: all of the API methods are the same, with only slight modifications to make it more JavaScript-y.
+
+For instance, in CouchDB you would fetch all documents using:
+
+```bash
+/db/_all_docs?include_docs=true
+```
+
+In PouchDB this becomes:
+
+```js
+db.allDocs({include_docs: true})
+```
+
+The APIs are the same, and the semantics are the same.
+
+In the following examples, we will set up CouchDB and talk to it using a tool you're already familiar with: your browser.
+
+{% include anchor.html title="Installing CouchDB" hash="installing-couchdb" %}
+
+If you are on a Debian flavor of Linux (Ubuntu, Mint, etc.), you can install CouchDB as follows.
+
+First, [enable the CouchDB package repository](https://docs.couchdb.org/en/stable/install/unix.html#enabling-the-apache-couchdb-package-repository) on your machine:
+
+```bash
+$ sudo apt update && sudo apt install -y curl apt-transport-https gnupg
+$ curl https://couchdb.apache.org/repo/keys.asc | gpg --dearmor | sudo tee /usr/share/keyrings/couchdb-archive-keyring.gpg >/dev/null 2>&1
+source /etc/os-release
+$ echo "deb [signed-by=/usr/share/keyrings/couchdb-archive-keyring.gpg] https://apache.jfrog.io/artifactory/couchdb-deb/ ${VERSION_CODENAME} main" \
+ | sudo tee /etc/apt/sources.list.d/couchdb.list >/dev/null
+```
+
+Next, update your package lists and install CouchDB:
+
+```bash
+$ sudo apt-get update
+$ sudo apt-get install -y couchdb
+```
+
+If you are on a Mac or Windows you should install the official binaries from [the CouchDB web site](https://couchdb.apache.org/#download).
+
+#### A CouchDB alternative: PouchDB Server
+
+If you have trouble installing CouchDB, you can also install PouchDB Server, which is a drop-in replacement for CouchDB that uses PouchDB under the hood:
+
+```bash
+$ npm install -g pouchdb-server
+$ pouchdb-server --port 5984
+```
+
+PouchDB Server is currently experimental, and we do not recommend it for production environments.
+
+{% include anchor.html title="Verify your installation" hash="verify-your–installation" %}
+
+Once CouchDB is installed, it should be running at `localhost:5984`. To verify, you can open up your terminal and type
+
+```bash
+$ curl localhost:5984
+```
+
+You should see something like:
+
+```js
+{"couchdb":"Welcome","version":"2.2.0",...}
+```
+
+Next, open up [http://localhost:5984/_utils/](http://localhost:5984/_utils/) in your browser.
+
+If you see a screen like the following, then you are ready to rock and roll with CouchDB:
+
+
+{% include img.html src="fauxton.png" alt="Fauxton interface" %}
+
+{% include anchor.html title="Set up CORS" hash="set-up-cors" %}
+
+[CORS](https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS) is a web technology that allows web sites to use resources from another domain. You will want to enable this in your CouchDB before continuing, because otherwise PouchDB will not work unless it's served from exactly the same domain as CouchDB.
+
+Enabling CORS is easy. Just install this handy script:
+
+```bash
+$ npm install -g add-cors-to-couchdb
+```
+
+And run it:
+
+```bash
+$ add-cors-to-couchdb
+```
+
+If you installed PouchDB Server, CORS is enabled by default, and this step is not necessary.
+
+{% include anchor.html title="Next" hash="next" %}
+
+Now that you have CouchDB installed, let's install PouchDB.
diff --git a/docs/version/9.0.0/guides/setup-pouchdb.md b/docs/version/9.0.0/guides/setup-pouchdb.md
new file mode 100644
index 0000000000..baea6a4551
--- /dev/null
+++ b/docs/version/9.0.0/guides/setup-pouchdb.md
@@ -0,0 +1,97 @@
+---
+index: 3
+layout: guide.html
+title: Setting up PouchDB
+sidebar: guides_nav.html
+---
+
+
+Installing PouchDB is easy. There are a few different ways to do it:
+
+{% include anchor.html title="Direct download" hash="direct-download" %}
+
+Download the latest **pouchdb-{{site.version}}.min.js** from the big green button above. Then in your `index.html`:
+
+```html
+
+```
+
+{% include anchor.html title="Bower" hash="bower" %}
+
+Run this on the command line:
+
+```bash
+$ bower install pouchdb
+```
+
+Then in your `index.html`:
+
+```html
+
+```
+
+{% include anchor.html title="npm" hash="npm" %}
+
+Run this on the command line:
+
+```bash
+$ npm install pouchdb
+```
+
+Then in your `index.html`:
+
+```html
+
+```
+
+{% include anchor.html title="jsdelivr CDN" hash="jsdelivr-cdn" %}
+
+Add this to your `index.html`:
+
+```html
+
+```
+
+{% include anchor.html title="Node.js" hash="nodejs" %}
+
+Run this on the command line:
+
+```bash
+$ npm install pouchdb
+```
+
+Then in your JavaScript:
+
+```js
+const PouchDB = require('pouchdb');
+```
+
+{% include anchor.html title="With TypeScript" hash="typescript" %}
+
+Run this on the command line:
+
+```bash
+$ npm install pouchdb @types/pouchdb
+```
+
+In your `tsconfig.json` activate `allowSyntheticDefaultImports`:
+
+```json
+{
+ "compilerOptions": {
+ "allowSyntheticDefaultImports": true
+ }
+}
+```
+
+Then in your TypeScript:
+
+```typescript
+import PouchDB from 'pouchdb';
+```
+
+You can install a plugin (provided there is a [type definition for it in npm](https://www.npmjs.com/search?q=scope:types%20pouchdb)), import it in the same way and then pass the imported name to `PouchDB.plugin()` method just as you would do in JavaScript.
+
+{% include anchor.html title="Next" hash="next" %}
+
+Now that you have PouchDB installed, let's start working with databases.
diff --git a/docs/version/9.0.0/guides/updating-deleting.md b/docs/version/9.0.0/guides/updating-deleting.md
new file mode 100644
index 0000000000..ba3b8b6c82
--- /dev/null
+++ b/docs/version/9.0.0/guides/updating-deleting.md
@@ -0,0 +1,145 @@
+---
+index: 7
+layout: guide.html
+nav: Updating/deleting documents
+title: Updating and deleting documents
+sidebar: guides_nav.html
+---
+
+As we saw in the past two chapters, working with PouchDB documents can be tricky, because you have to manage the revision identifier `_rev`.
+
+Now that we understand promises, though, there are few techniques we can use to make our code more elegant and readable.
+
+{% include anchor.html title="Creating a default document" hash="creating-a–default-document" %}
+
+Often in our code, we'll want to `get()` a document, and if it doesn't exist, we want to create some default.
+
+For instance, let's say we have a configuration object. We want to provide some reasonable defaults for our config:
+
+```js
+{
+ _id: 'config',
+ background: 'blue',
+ foreground: 'white',
+ sparkly: 'false'
+}
+```
+
+This is a pretty good default setting! So let's write the code to set it as our default.
+
+Thankfully, promises make this rather easy:
+
+```js
+db.get('config').catch(function (err) {
+ if (err.name === 'not_found') {
+ return {
+ _id: 'config',
+ background: 'blue',
+ foreground: 'white',
+ sparkly: 'false'
+ };
+ } else { // hm, some other error
+ throw err;
+ }
+}).then(function (configDoc) {
+ // sweet, here is our configDoc
+}).catch(function (err) {
+ // handle any errors
+});
+```
+
+This code is doing the following:
+
+* Try to `get()` a doc with `_id` equal to `'config'`
+* If it doesn't find it, return the default doc
+* Otherwise, you'll just get back the existing document
+
+You can see **[a live example](http://bl.ocks.org/nolanlawson/0a01d466b2d331cf7e25)** of this code.
+
+{% include anchor.html title="Why must we dance this dance?" hash="why-must-we-dance-this-dance" %}
+
+A common question from new PouchDB/CouchDB users is: why do we have to deal with `_rev` at all? Why can't I just `put()` the document without providing a `_rev`?
+
+The answer is: because `_rev`s are what makes sync work so well. PouchDB asks for a little upfront effort with managing document revisions, so that later on, sync is a breeze.
+
+In fact, you are probably already familiar with a system that forces you to go through a similar dance. This system is called [Git](http://www.git-scm.com/).
+
+PouchDB and CouchDB's document revision structure is very similar to Git's. In fact, each document's revision history is stored as a tree (exactly like Git), which allows you to handle conflicts when any two databases get out of sync.
+
+```
+rev 3-a rev 3-b
+ \___/
+ |
+ rev 2
+ |
+ rev 1
+```
+
+Conflicts will be discussed later in this guide. For now, you can think of revisions as being a single lineage:
+
+```
+ rev 4
+ |
+ rev 3
+ |
+ rev 2
+ |
+ rev 1
+```
+
+{% include anchor.html title="Deleting documents" hash="deleting-documents" %}
+
+When you `remove()` a document, it's not really deleted; it just gets a `_deleted` attribute added to it.
+
+That is, the database saves a tombstone at the end of the revision tree.
+
+```
+{_id: 'foo', _rev: '4-z', _deleted: true}
+ |
+{_id: 'foo', _rev: '3-y'}
+ |
+{_id: 'foo', _rev: '2-x'}
+ |
+{_id: 'foo', _rev: '1-w'}
+```
+
+There are three ways of deleting a document, which are all equivalent:
+
+1) You can call `db.remove(doc)`:
+
+```js
+db.get('mydoc').then(function (doc) {
+ return db.remove(doc);
+});
+```
+
+2) You can call `db.remove(doc._id, doc._rev)`:
+
+```js
+db.get('mydoc').then(function (doc) {
+ return db.remove(doc._id, doc._rev);
+});
+```
+
+3) You can call `db.put(doc)` with `_deleted` set to `true`:
+
+```js
+db.get('mydoc').then(function (doc) {
+ doc._deleted = true;
+ return db.put(doc);
+});
+```
+
+Of course, you will want to add `catch()` to the end of all these, unless you like to live dangerously.
+
+You can see **[a live example](http://bl.ocks.org/nolanlawson/b2049ad69308e92f15bc)** of this code.
+
+{% include anchor.html title="Related API documentation" hash="related-api-documentation" %}
+
+* [get()](/api.html#fetch_document)
+* [put()](/api.html#create_document)
+* [remove()](/api.html#delete_document)
+
+{% include anchor.html title="Next" hash="next" %}
+
+Now that we understand how to update and delete documents, let's do it in bulk.
diff --git a/docs/version/9.0.0/learn.md b/docs/version/9.0.0/learn.md
new file mode 100644
index 0000000000..d23ac36335
--- /dev/null
+++ b/docs/version/9.0.0/learn.md
@@ -0,0 +1,38 @@
+---
+layout: 2ColLeft.html
+title: About PouchDB
+sidebar: ./nav.html
+---
+
+PouchDB is an **in-browser database** that allows applications to save data locally, so that users can enjoy all the features of an app even when they're offline. Plus, the data is synchronized between clients, so users can stay up-to-date wherever they go.
+
+PouchDB also runs in **Node.js** and can be used as a direct interface to **CouchDB**-compatible servers. The API works the same in every environment, so you can spend less time worrying about browser differences, and more time writing clean, consistent code.
+
+PouchDB is a free open-source project, written in JavaScript and driven by our [wonderful community](https://github.com/apache/pouchdb/graphs/contributors). If you want to get involved, then check out the [contributing guide](https://github.com/apache/pouchdb/blob/master/CONTRIBUTING.md).
+
+{% include anchor.html class="h3" title="Browser Support" hash="browser_support" %}
+
+PouchDB supports all modern browsers, using [IndexedDB][] under the hood and falling back to [WebSQL][] where IndexedDB isn't supported. It is [fully tested](https://github.com/apache/pouchdb/actions) and supported in:
+
+ * Firefox 29+ (Including Firefox OS and Firefox for Android)
+ * Chrome 30+
+ * Safari 5+
+ * Internet Explorer 10+
+ * Opera 21+
+ * Android 4.0+
+ * iOS 7.1+
+ * Windows Phone 8+
+
+PouchDB also runs in [Cordova/PhoneGap](https://github.com/nolanlawson/pouchdb-phonegap-cordova), [NW.js](https://github.com/nolanlawson/pouchdb-nw), [Electron](https://github.com/nolanlawson/pouchdb-atom-shell), and [Chrome apps](https://github.com/nolanlawson/pouchdb-chrome-app). It is framework-agnostic, and you can use it with Angular, React, Ember, Backbone, or your framework of choice. There are [many adapters]({{ site.baseurl }}/external.html#framework_adapters), or you can just use PouchDB as-is.
+
+PouchDB requires a modern ES5 environment, so if you need to support older browsers (IE <10, Android <4.0, Opera Mini), then you should include the [es5-shim](https://github.com/es-shims/es5-shim) library. You can also use the [LocalStorage and in-memory adapters](/adapters.html#pouchdb_in_the_browser), or fall back to a live CouchDB.
+
+{% include anchor.html class="h3" title="Node.js" hash="node_js" %}
+
+In Node.js, PouchDB uses [LevelDB][] under the hood, and also supports [many other backends](/adapters.html#pouchdb_in_node_js) via the [LevelUP ecosystem](https://github.com/rvagg/node-levelup).
+
+PouchDB can also run as its own CouchDB-compatible web server, using [PouchDB Server](https://github.com/pouchdb/pouchdb-server).
+
+[IndexedDB]: http://caniuse.com/#feat=indexeddb
+[WebSQL]: http://caniuse.com/#feat=sql-storage
+[LevelDB]: https://github.com/google/leveldb
diff --git a/docs/version/9.0.0/offline.md b/docs/version/9.0.0/offline.md
new file mode 100644
index 0000000000..105fbc1a23
--- /dev/null
+++ b/docs/version/9.0.0/offline.md
@@ -0,0 +1,11 @@
+---
+layout: default.html
+title: Looks like you're offline...
+edit: false
+---
+
+
+
+
You need to be online to see this page, once you've seen it once it'll be available offline in the future!
+
+
diff --git a/docs/version/9.0.0/users.md b/docs/version/9.0.0/users.md
new file mode 100644
index 0000000000..b9aed7570a
--- /dev/null
+++ b/docs/version/9.0.0/users.md
@@ -0,0 +1,132 @@
+---
+layout: 2ColLeft.html
+title: Who's using PouchDB?
+sidebar: ./nav.html
+---
+
+A list of known products and services that are using PouchDB.
+## Newt
+
+{% include img.html width=100 src="newt.png" alt="Newt" %}
+
+[Newt](https://newt.to) lets you read, listen and write thousands of books across devices natively. It runs on Windows, Linux, macOS, Android, iOS & web. Its backed is powered by CouchDB and it uses PouchDB on the web client, desktop client and mobile native app for offline access, live sync, revisions and much more.
+
+## BikeCommute
+
+[BikeCommute](https://github.com/autonome/bikecommute) is a FirefoxOS app that registers an NFC tag to track bike commuters in the Mozilla Portland office. Built using Famo.us, PouchDB, and CouchDB. See [a video of it in action](https://youtu.be/3BVZYcQ-TYA) or [read about it on Mozilla Hacks](https://hacks.mozilla.org/2014/11/nfc-in-firefox-os/).
+
+## Cloudwall
+
+{% include img.html width=200 src="cloudwall.png" alt="Cloudwall" %}
+
+[Cloudwall](http://cloudwall.me/) is an operating system for noBackend webapps, based on CouchDB and PouchDB.
+
+## Cozy Cloud
+
+{% include img.html width=125 src="cozy.png" alt="Cozy Cloud" %}
+
+[Cozy](https://cozy.io/en/) is a personal cloud that you can host, customize, and fully control. It syncs contacts, calendars, and files between your personal devices and server. Under the hood, it leverages CouchDB and PouchDB.
+
+## Delta
+[Delta](https://github.com/octavore/delta) is a command-line utility for text diffs. View split diffs in the browser with syntax highlighting, or in the command-line using the --cli flag.
+
+## DevITJobs
+[DevITJobs](http://devitjobs.com) is a job board for the tech industry with mandatory salary ranges & tech stacks.
+
+## eHealth Africa
+
+{% include img.html width=200 src="ehealth_africa.png" alt="eHealth Africa" %}
+
+[eHealth Africa](http://ehealthafrica.org/) is an American-Nigerian NGO specialising in the development and deployment of tech for health. To tackle the Ebola outbreak, they built [mobile apps and dashboards](https://github.com/eHealthAfrica) to help track the spread of infection in the field. The combination of CouchDB and PouchDB enabled these apps to work consistently despite the extreme network unreliability of sub-saharan Africa.
+
+## Financier
+
+[Financier](https://financier.io) is a freemium personal budgeting app that uses PouchDB to store your budget data. The paid version syncs data with CouchDB 2 for data persistence and sharing across devices. The app is fully integrated with the PouchDB changes feed for instant updates. More about the stack is available in [humans.txt](https://app.financier.io/humans.txt).
+
+## GRADEpro GDT
+
+{% include img.html width=150 src="gradepro.png" alt="GRADEpro GDT" href="http://gradepro.org/" %}
+
+[GRADEpro GDT](http://gradepro.org/) is an easy to use, all‐in‐one web solution to summarize and present information for healthcare decision making. PouchDB is instrumental for both the collaboration features of GRADEpro (so many users can work on the same data simultaneously) and implementation of the offline mode. The tool is used in more than 135 countries, some of which have poor Internet connectivity, as well as by scientists, who are frequent travelers and need to use the application e.g. on a plane.
+
+## Hoodie
+
+{% include img.html width=150 src="HoodieLogo.png" alt="Hoodie" href="http://hood.ie/" %}
+
+[Hoodie](http://hood.ie/) provides a complete backend solution for your frontend code. It helps you develop your web application fast and easy. Hoodie-based apps are [offline-first](http://offlinefirst.org/) so they are usable anytime. Just plug Hoodie’s API into your frontend code, and your app is ready.
+
+## HospitalRun
+
+{% include img.html width=150 src="hospitalrun-logo.svg" alt="HospitalRun" href="http://hospitalrun.io/" %}
+
+[HospitalRun](http://hospitalrun.io/) is an open source software product designed specifically for developing world hospitals, making usability the key requirement. Using PouchDB and offline-first design, HospitalRun allows records to be carried to remote clinics, functioning when there is no Internet, and syncing when there is.
+
+## Local NPM
+
+[Local NPM](https://github.com/nolanlawson/local-npm) allows you to easily set up a local NPM server that caches data from the real NPM and updates in realtime. The goal is to cut down network request time by moving data closer to the client. Built using Node.js, LevelDB, and PouchDB.
+
+## Lullabot
+
+[Lullabot](https://www.lullabot.com) is an interactive strategy, design, and development company. We create delightful experiences using Drupal and open source technologies. Our main website is using Drupal as the back-end for content management, CouchDB with PouchDB for the API, and ReactJS for the front-end.
+
+## MBTA Alerts
+
+{% include img.html width=100 src="mbta_alerts.jpeg" alt="MBTA Alerts" %}
+
+[MBTA Alerts](https://twitter.com/MBTA_Alerts) is a Twitter bot that thousands of Bostonians depend upon to be notified of delays in the MBTA transit system. Uses Node.js, PouchDB, and CouchDB.
+
+## Medic Mobile
+
+{% include img.html src="medic-mobile.png" alt="Medic Mobile" %}
+
+[Medic Mobile](http://medic.org) is a software toolkit that combines smart messaging, decision support, easy data gathering and management, and health system analytics. Thanks to PouchDB our tools are offline first so workers can file reports wherever they are and sync when back online.
+
+## MoneyTracker.cc
+
+[MoneyTracker](https://moneytracker.cc/) is an open-source personal finances tracking web app. This app can work offline on desktop, tablet and mobile.
+Data is stored locally on device in PouchDB database and can be synced to the cloud.
+
+## NPM Browser
+
+[NPM Browser](https://github.com/pouchdb/npm-browser) is a fully offline cache of NPM packages, which runs in your browser, using Angular.js, PouchDB, and the pouchdb-load plugin.
+
+## Pokedex.org
+
+{% include img.html width=100 src="pokedexorg-logo.png" alt="Pokedex.org logo" %}
+
+[Pokedex.org](https://github.com/nolanlawson/pokedex.org) is a progressive web app, powered by ServiceWorker, PouchDB, virtual-dom, and web workers.
+
+## Quizster
+
+{% include img.html width=100 src="quizster.svg" alt="Quizster" href="https://quizster.co" %}
+
+[Quizster](https://quizster.co) is a photo-based submission and feedback system. Quizster uses PouchDB to allow real-time communication between students and teachers.
+
+## Squarespace Blog
+
+{% include img.html width=100 src="squarespace_blog.png" alt="Squarespace Blog" %}
+
+Squarespace Blog is an [Android](https://play.google.com/store/apps/details?id=com.squarespace.android.blog) and [iOS app](https://itunes.apple.com/us/app/squarespace-blog/id715084234) that gives you the tools you need to write and edit posts on multiple Squarespace websites. It uses PouchDB attachments inside a WebView for fast offline images.
+
+## Story-writer
+
+Story-writer is a free Markdown editor, with a [web version](http://markdown.xiaoshujiang.com) and [NW.js client version](http://soft.xiaoshujiang.com), built using Node.js, NW.js, LevelDB, and PouchDB.
+
+## Thali project
+
+[Thali](http://thaliproject.org/) is a Microsoft-sponsored open-source platform for creating apps that exploit the power of personal devices and put people in control of their data. It uses Cordova, PouchDB, OpenSSL, and Tor.
+
+## StudyMD
+
+{% include img.html width=100 src="studymd.png" alt="StudyMD" %}
+
+[StudyMD](https://github.com/jotron/StudyMD) transforms your markdown files into flashcards. It's an electronjs app based on reactjs. PouchDB is used in Node.js for storing the flashcards in a LevelDB database.
+
+## YLD
+
+[YLD](http://www.yld.io) is a Node.js software engineering, consulting and training company. We partner with enterprises to strengthen their software engineering culture and create the agility necessary to compete in today’s market, and are responsible for some of the largest Node.js solutions in production today. PouchDB allows us to create responsive, resilient and sync-enabled web applications.
+
+## FactoryTalk TeamONE
+
+{% include img.html src="TeamONE.png" alt="FactoryTalk TeamONE" %}
+[FactoryTalk® TeamONE™](http://33seconds.io) delivers one new experience and helps industrial teams improve productivity. Created by [Rockwell Automation](http://www.rockwellautomation.com/). We're using PouchDB for offline access and team synchronization. Looking to use the [Thali project](http://thaliproject.org/) (also a PouchDB client listed here) to enable true peer-to-peer connectivity. Connect with us on twitter ([@ROKTeamONE](https://twitter.com/rokteamone).)
diff --git a/docs/version/latest/adapters.md b/docs/version/latest/adapters.md
new file mode 100644
index 0000000000..cf09c5dd44
--- /dev/null
+++ b/docs/version/latest/adapters.md
@@ -0,0 +1,220 @@
+---
+layout: 2ColLeft.html
+title: Adapters
+sidebar: ./nav.html
+---
+
+PouchDB is not a self-contained database; it is a CouchDB-style abstraction layer over other databases. By default, PouchDB ships with the [IndexedDB][] adapter for the browser, and a [LevelDB][] adapter in Node.js. This can be visualized as so:
+
+
+
+PouchDB attempts to provide a consistent API that "just works" across every browser and JavaScript environment, and in most cases, you can just use the defaults. However, if you're trying to reach the widest possible audience, or if you want the best performance, then you will sometimes want to tinker with the adapter settings.
+
+#### Topics:
+* [PouchDB in the browser](#pouchdb_in_the_browser)
+* [PouchDB in Node.js](#pouchdb_in_node_js)
+* [PouchDB over HTTP](#pouchdb_over_http)
+* [More resources](#more_resources)
+
+
+{% include anchor.html title="PouchDB in the browser" hash="pouchdb_in_the_browser"%}
+
+In the browser, PouchDB prefers IndexedDB.
+
+{% include alert/start.html variant="info"%}
+Prior to PouchDB 7.0.0, the WebSQL adapter was used for Safari/iOS. The WebSQL adapter no longer ships in PouchDB, but may be installed separately.
+{% include alert/end.html%}
+
+If you're ever curious which adapter is being used in a particular browser, you can use the following method:
+
+```js
+const pouch = new PouchDB('myDB');
+console.log(pouch.adapter); // prints 'idb'
+```
+
+### SQLite plugin for Cordova/PhoneGap
+
+On Cordova/PhoneGap/Ionic, the native SQLite database is often a popular choice, because it allows unlimited storage (compared to [IndexedDB/WebSQL storage limits](http://www.html5rocks.com/en/tutorials/offline/quota-research)). It also offers more flexibility in backing up and pre-loading databases, because the SQLite files are directly accessible to app developers.
+
+There are various Cordova plugins that can provide access to native SQLite, such as
+[Cordova-sqlite-storage](https://github.com/litehelpers/Cordova-sqlite-storage),
+[cordova-plugin-sqlite-2](https://github.com/nolanlawson/cordova-plugin-sqlite-2), or
+[cordova-plugin-websql](https://github.com/Microsoft/cordova-plugin-websql).
+
+To use them, you must install them separately into your Cordova application, and then add a special third-party PouchDB adapter
+called [pouchdb-adapter-cordova-sqlite](https://github.com/nolanlawson/pouchdb-adapter-cordova-sqlite). Once you do
+that, you can use it via:
+
+```js
+const db = new PouchDB('myDB.db', {adapter: 'cordova-sqlite'});
+```
+
+{% include alert/start.html variant="info"%}
+In PouchDB pre-6.0.0, Cordova SQLite support was available out-of-the-box, but it has been moved to a separate plugin
+to reduce confusion and to make it explicit whether you are using WebSQL or Cordova SQLite.
+{% include alert/end.html%}
+
+We recommend avoiding Cordova SQLite unless you are hitting the 50MB storage limit in iOS, you
+require native or preloaded access to the database files, or there's some other reason to go native.
+The built-in IndexedDB adapter is nearly always more performant and stable.
+
+### Browser adapter plugins
+
+PouchDB also offers separate browser plugins that use backends other than IndexedDB. These plugins fully pass the PouchDB test suite and are rigorously tested in our CI process.
+
+**Downloads:**
+
+* [pouchdb.memory.js](https://github.com/apache/pouchdb/releases/download/{{ site.version }}/pouchdb.memory.js) (Minified: [pouchdb.memory.min.js](https://github.com/apache/pouchdb/releases/download/{{ site.version }}/pouchdb.memory.min.js))
+* [pouchdb.localstorage.js](https://github.com/apache/pouchdb/releases/download/{{ site.version }}/pouchdb.localstorage.js) (Minified: [pouchdb.localstorage.min.js](https://github.com/apache/pouchdb/releases/download/{{ site.version }}/pouchdb.localstorage.min.js))
+
+{% include alert/start.html variant="warning"%}
+{% markdown %}
+These plugins add a hefty footprint due to external dependencies, so take them with a grain of salt.
+{% endmarkdown %}
+{% include alert/end.html%}
+
+#### In-memory adapter
+
+If you want a quick database for your unit tests, you can use the `pouchdb.memory.js` plugin, which offers a pure in-memory PouchDB:
+
+```html
+
+
+
+```
+
+This pouch will act exactly like a normal one – replicating, storing attachments, pagination, etc. – but it will be deleted as soon as the user closes their browser. However, multiple `PouchDB` objects with the same database name will share the same data:
+
+```js
+// pouch1 and pouch2 will share the same data
+const pouch1 = new PouchDB('myDB', {adapter: 'memory'});
+const pouch2 = new PouchDB('myDB', {adapter: 'memory'});
+
+// pouch3 will have its own data
+const pouch3 = new PouchDB('myOtherDB', {adapter: 'memory'});
+```
+
+#### LocalStorage adapter
+
+If you need to support very old browsers, such as IE ≤ 9.0 and Opera Mini, you can use the `pouchdb.localstorage.js` plugin, which allows PouchDB to fall back to [LocalStorage][] on browsers that don't support either IndexedDB or WebSQL. The [es5-shims][] will also be necessary.
+
+```html
+
+
+
+```
+
+{% include alert/start.html variant="warning"%}
+The LocalStorage plugin should be considered highly experimental, and the underlying structure may change in the future. Currently it stores all document IDs in memory, which works fine on small databases but may crash on larger databases. You can follow localstorage-down to track our progress.
+{% include alert/end.html %}
+
+{% include anchor.html title="PouchDB in Node.js" hash="pouchdb_in_node_js"%}
+
+#### In-memory
+
+{% include alert/start.html variant="warning"%}
+{% markdown %}
+**Warning: deprecation notice.** The `memory` adapter will be deprecated in PouchDB version 10.0.0 and removed in version 11.0.0. You can read [the migration guide here](https://pouchdb.com/2026/04/10/migration-to-nodesqlite.html) and more about the topic in [this link](https://github.com/apache/pouchdb/issues/9163).
+{% endmarkdown %}
+{% include alert/end.html%}
+
+Just as in the browser, you can also create a pure in-memory PouchDB:
+
+```bash
+$ npm install pouchdb-adapter-memory
+```
+
+then:
+
+```js
+PouchDB.plugin(require('pouchdb-adapter-memory'));
+const pouch = new PouchDB('myDB', {adapter: 'memory'});
+```
+
+This implementation is based on [MemDOWN](https://github.com/level/memdown), and will not write any changes to disk.
+
+#### Node SQLite adapter
+
+You can also use PouchDB in Node.js' [native SQLite module](https://nodejs.org/api/sqlite.html), when using Node.js' `>22.5.0` version.
+
+```js
+const PouchDB = require('pouchdb');
+PouchDB.plugin(require('pouchdb-adapter-node-sqlite'));
+
+const db = new PouchDB('mydatabase.db', {adapter: 'nodesqlite'});
+```
+
+#### Other LevelDOWN adapters
+
+{% include alert/start.html variant="warning"%}
+{% markdown %}
+**Warning: deprecation notice.** The `leveldb` adapter will be deprecated in PouchDB version 10.0.0 and removed in version 11.0.0. You can read [the migration guide here](https://pouchdb.com/2026/04/10/migration-to-nodesqlite.html) and more about the topic in [this link](https://github.com/apache/pouchdb/issues/9163).
+{% endmarkdown %}
+{% include alert/end.html%}
+
+Technically you are free to use
+[any LevelDOWN-based implementation](https://github.com/rvagg/node-levelup/wiki/Modules#storage-back-ends) in either Node or the browser.
+However this should be considered **extremely experimental** and not designed for production use.
+
+See [pouchdb-adapter-leveldb-core](https://www.npmjs.com/package/pouchdb-adapter-leveldb-core) for details.
+
+{% include anchor.html title="PouchDB over HTTP" hash="pouchdb_over_http"%}
+
+In both the browser and in Node.js, PouchDB can also function as a straightforward API on top of any [CouchDB](https://couchdb.apache.org/)-compliant database:
+
+```js
+const pouch = new PouchDB('http://my-site.com:5984/my-db');
+const securePouch = new PouchDB('https://my-secure-site.com:5984/my-secure-db');
+```
+
+You can also sync to and from these databases to your local PouchDB.
+
+Currently PouchDB has full support for:
+
+* CouchDB 1.x
+* [Smileupps](https://www.smileupps.com/) (same as 1.x)
+* CouchDB 2.x ([tested in CI](https://github.com/apache/pouchdb/actions))
+* CouchDB 3.x ([tested in CI](https://github.com/apache/pouchdb/actions))
+* [Cloudant](https://cloudant.com/) (roughly the same as 2.x)
+* [PouchDB Server](https://github.com/pouchdb/pouchdb-server) ([tested in CI](https://github.com/apache/pouchdb/actions))
+* [PouchDB Server --in-memory mode](https://github.com/pouchdb/pouchdb-server)
+
+[Drupal 8](http://wearepropeople.com/blog/a-content-staging-solution-for-drupal-8-and-more) has also announced support for PouchDB, and there is [rcouch](https://github.com/rcouch/rcouch) as well, but these are both untested by PouchDB.
+
+If you are ever unsure about a server, consider replicating from PouchDB to CouchDB, then from that CouchDB to the other server.
+
+#### PouchDB Server
+
+[PouchDB Server](https://github.com/pouchdb/pouchdb-server) is a standalone REST server that implements the CouchDB API, while using a LevelDB-based PouchDB under the hood. It also supports an `--in-memory` mode and any [LevelDOWN][] adapter, which you may find handy.
+
+PouchDB Server passes the PouchDB test suite at 100%, but be aware that it is not as full-featured or battle-tested as CouchDB.
+
+#### PouchDB Express
+
+The underlying module for PouchDB Server, [Express PouchDB](https://github.com/pouchdb/express-pouchdb) is an Express submodule that mimics most of the CouchDB API within your Express application.
+
+{% include anchor.html title="More resources" hash="more_resources"%}
+
+The best place to look for information on which browsers support which databases is [caniuse.com](http://caniuse.com). You can consult their tables on browser support for various backends:
+
+* [IndexedDB](http://caniuse.com/indexeddb)
+* [WebSQL](http://caniuse.com/sql-storage)
+* [LocalStorage](http://caniuse.com/namevalue-storage)
+
+[IndexedDB]: http://www.w3.org/TR/IndexedDB/
+[WebSQL]: http://www.w3.org/TR/webdatabase/
+[LevelDB]: https://code.google.com/p/leveldb/
+[LocalStorage]: https://developer.mozilla.org/en-US/docs/Web/Guide/API/DOM/Storage
+[es5-shims]: https://github.com/es-shims/es5-shim
+[sqlite plugin]: https://github.com/brodysoft/Cordova-SQLitePlugin
+[sqlite plugin 2]: https://github.com/nolanlawson/cordova-plugin-sqlite-2
+[leveldown]: https://github.com/rvagg/node-leveldown
+[level-js]: https://github.com/maxogden/level.js
diff --git a/docs/version/latest/api.html b/docs/version/latest/api.html
new file mode 100644
index 0000000000..e7d2e9d777
--- /dev/null
+++ b/docs/version/latest/api.html
@@ -0,0 +1,42 @@
+---
+layout: 2ColLeft.html
+title: API Reference
+sidebar: api.html
+edit: false
+---
+
+{% markdown %}
+
+{% include ./api/overview.html %}
+{% include ./api/create_database.html %}
+{% include ./api/delete_database.html %}
+{% include ./api/create_document.html %}
+{% include ./api/fetch_document.html %}
+{% include ./api/delete_document.html %}
+{% include ./api/batch_create.html %}
+{% include ./api/batch_fetch.html %}
+{% include ./api/changes.html %}
+{% include ./api/replication.html %}
+{% include ./api/sync.html %}
+{% include ./api/save_attachment.html %}
+{% include ./api/get_attachment.html %}
+{% include ./api/delete_attachment.html %}
+{% include ./api/create_index.html %}
+{% include ./api/query_index.html %}
+{% include ./api/explain_index.html %}
+{% include ./api/list_indexes.html %}
+{% include ./api/delete_index.html %}
+{% include ./api/query_database.html %}
+{% include ./api/view_cleanup.html %}
+{% include ./api/database_information.html %}
+{% include ./api/compaction.html %}
+{% include ./api/revisions_diff.html %}
+{% include ./api/bulk_get.html %}
+{% include ./api/close_database.html %}
+{% include ./api/purge.html %}
+{% include ./api/events.html %}
+{% include ./api/active_tasks.html %}
+{% include ./api/defaults.html %}
+{% include ./api/plugins.html %}
+
+{% endmarkdown %}
diff --git a/docs/version/latest/api/active_tasks.html b/docs/version/latest/api/active_tasks.html
new file mode 100644
index 0000000000..673a8a8850
--- /dev/null
+++ b/docs/version/latest/api/active_tasks.html
@@ -0,0 +1,45 @@
+{% include anchor.html edit="true" title="List active tasks" hash="active_tasks" %}
+
+{% highlight js %}
+PouchDB.activeTasks.list()
+{% endhighlight %}
+
+List all active database tasks. There are three types of internal tasks: `database_compaction`, `view_indexing`, and `replication`. PouchDB will report progress of these tasks to the active tasks API and remove tasks as soon as they are completed or have failed.
+
+#### Example Usage:
+
+{% highlight js %}
+const tasks = PouchDB.activeTasks.list()
+{% endhighlight %}
+
+#### Example Result:
+
+{% highlight js %}
+[{
+ "id": "d81fea92-8ce4-42df-bb2b-89a4e67536c3",
+ "name": "database_compaction",
+ "created_at": "2022-02-08T15:38:45.318Z",
+ "total_items": 12,
+ "completed_items": 1,
+ "updated_at": "2022-02-08T15:38:45.821Z"
+}]
+{% endhighlight %}
+
+### Real-time updates
+
+You can use [JavaScript Proxies](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy) to monitor calls to the active tasks API. For the `PouchDB.activeTasks.add()` function, which is used internally to announce new tasks to PouchDB, you can monitor calls as follows:
+
+{% highlight js %}
+PouchDB.activeTasks.add = new Proxy(PouchDB.activeTasks.add, {
+ apply: (target, thisArg, argumentsList) => {
+ const task = argumentsList[0];
+ const id = Reflect.apply(
+ target,
+ PouchDB.activeTasks,
+ [task]
+ );
+ console.log('Added task', id, task.name);
+ return id;
+ },
+});
+{% endhighlight %}
diff --git a/docs/version/latest/api/batch_create.html b/docs/version/latest/api/batch_create.html
new file mode 100644
index 0000000000..13f58a6abf
--- /dev/null
+++ b/docs/version/latest/api/batch_create.html
@@ -0,0 +1,271 @@
+{% include anchor.html edit="true" title="Create/update a batch of documents" hash="batch_create" %}
+
+{% highlight js %}
+db.bulkDocs(docs, [options], [callback])
+{% endhighlight %}
+
+Create, update or delete multiple documents. The `docs` argument is an array of documents.
+
+If you omit an `_id` parameter on a given document, the database will create a new document and assign the ID for you. To update a document, you must include both an `_id` parameter and a `_rev` parameter, which should match the ID and revision of the document on which to base your updates. Finally, to delete a document, include a `_deleted` parameter with the value `true`.
+
+#### Example Usage:
+
+Put some new docs, providing the `_id`s:
+
+{% include code/start.html id="bulk_docs_1" type="callback" %}
+{% highlight js %}
+db.bulkDocs([
+ {title : 'Lisa Says', _id: 'doc1'},
+ {title : 'Space Oddity', _id: 'doc2'}
+], function(err, response) {
+ if (err) { return console.log(err); }
+ // handle result
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="bulk_docs_1" type="async" %}
+{% highlight js %}
+try {
+ const result = await db.bulkDocs([
+ {title : 'Lisa Says', _id: 'doc1'},
+ {title : 'Space Oddity', _id: 'doc2'}
+ ]);
+} catch (err) {
+ console.log(err);
+}
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="bulk_docs_1" type="promise" %}
+{% highlight js %}
+db.bulkDocs([
+ {title : 'Lisa Says', _id: 'doc1'},
+ {title : 'Space Oddity', _id: 'doc2'}
+]).then(function (result) {
+ // handle result
+}).catch(function (err) {
+ console.log(err);
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+Post some new docs and auto-generate the `_id`s:
+
+{% include code/start.html id="bulk_docs_2" type="callback" %}
+{% highlight js %}
+db.bulkDocs([
+ {title : 'Lisa Says'},
+ {title : 'Space Oddity'}
+], function(err, response) {
+ if (err) { return console.log(err); }
+ // handle result
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="bulk_docs_2" type="async" %}
+{% highlight js %}
+try {
+ const result = await db.bulkDocs([
+ {title : 'Lisa Says'},
+ {title : 'Space Oddity'}
+ ]);
+} catch (err) {
+ console.log(err);
+}
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="bulk_docs_2" type="promise" %}
+{% highlight js %}
+db.bulkDocs([
+ {title : 'Lisa Says'},
+ {title : 'Space Oddity'}
+]).then(function (result) {
+ // handle result
+}).catch(function (err) {
+ console.log(err);
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+#### Example Response:
+{% highlight js %}
+[
+ {
+ "ok": true,
+ "id": "doc1",
+ "rev": "1-84abc2a942007bee7cf55007cba56198"
+ },
+ {
+ "ok": true,
+ "id": "doc2",
+ "rev": "1-7b80fc50b6af7a905f368670429a757e"
+ }
+]
+{% endhighlight %}
+
+The response contains an array of the familiar `ok`/`rev`/`id`
+from the [put()/post() API](#create_document). If there are any errors, they
+will be provided individually like so:
+
+{% highlight js %}
+[
+ { status: 409,
+ name: 'conflict',
+ message: 'Document update conflict',
+ error: true
+ }
+]
+{% endhighlight %}
+
+The results are returned in the same order as the supplied "docs" array.
+
+Note that `bulkDocs()` is not transactional, and that you may get
+back a mixed array of errors/non-errors. In CouchDB/PouchDB, the smallest
+atomic unit is the document.
+
+#### Bulk update/delete:
+
+You can also use `bulkDocs()` to update/delete many documents at once:
+
+{% include code/start.html id="bulk_docs3" type="callback" %}
+{% highlight js %}
+db.bulkDocs([
+ {
+ title : 'Lisa Says',
+ artist : 'Velvet Underground',
+ _id : "doc1",
+ _rev : "1-84abc2a942007bee7cf55007cba56198"
+ },
+ {
+ title : 'Space Oddity',
+ artist : 'David Bowie',
+ _id : "doc2",
+ _rev : "1-7b80fc50b6af7a905f368670429a757e"
+ }
+], function(err, response) {
+ if (err) { return console.log(err); }
+ // handle result
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="bulk_docs3" type="async" %}
+{% highlight js %}
+try {
+ const result = await db.bulkDocs([
+ {
+ title : 'Lisa Says',
+ artist : 'Velvet Underground',
+ _id : "doc1",
+ _rev : "1-84abc2a942007bee7cf55007cba56198"
+ },
+ {
+ title : 'Space Oddity',
+ artist : 'David Bowie',
+ _id : "doc2",
+ _rev : "1-7b80fc50b6af7a905f368670429a757e"
+ }
+ ]);
+} catch (err) {
+ console.log(err);
+}
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="bulk_docs3" type="promise" %}
+{% highlight js %}
+db.bulkDocs([
+ {
+ title : 'Lisa Says',
+ artist : 'Velvet Underground',
+ _id : "doc1",
+ _rev : "1-84abc2a942007bee7cf55007cba56198"
+ },
+ {
+ title : 'Space Oddity',
+ artist : 'David Bowie',
+ _id : "doc2",
+ _rev : "1-7b80fc50b6af7a905f368670429a757e"
+ }
+]).then(function (result) {
+ // handle result
+}).catch(function (err) {
+ console.log(err);
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+Or delete them:
+
+{% include code/start.html id="bulk_docs_4" type="callback" %}
+{% highlight js %}
+db.bulkDocs([
+ {
+ title : 'Lisa Says',
+ _deleted : true,
+ _id : "doc1",
+ _rev : "1-84abc2a942007bee7cf55007cba56198"
+ },
+ {
+ title : 'Space Oddity',
+ _deleted : true,
+ _id : "doc2",
+ _rev : "1-7b80fc50b6af7a905f368670429a757e"
+ }
+], function(err, response) {
+ if (err) { return console.log(err); }
+ // handle result
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="bulk_docs_4" type="async" %}
+{% highlight js %}
+try {
+ const result = await db.bulkDocs([
+ {
+ title : 'Lisa Says',
+ _deleted : true,
+ _id : "doc1",
+ _rev : "1-84abc2a942007bee7cf55007cba56198"
+ },
+ {
+ title : 'Space Oddity',
+ _deleted : true,
+ _id : "doc2",
+ _rev : "1-7b80fc50b6af7a905f368670429a757e"
+ }
+ ]);
+} catch (err) {
+ console.log(err);
+}
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="bulk_docs_4" type="promise" %}
+{% highlight js %}
+db.bulkDocs([
+ {
+ title : 'Lisa Says',
+ _deleted : true,
+ _id : "doc1",
+ _rev : "1-84abc2a942007bee7cf55007cba56198"
+ },
+ {
+ title : 'Space Oddity',
+ _deleted : true,
+ _id : "doc2",
+ _rev : "1-7b80fc50b6af7a905f368670429a757e"
+ }
+]).then(function (result) {
+ // handle result
+}).catch(function (err) {
+ console.log(err);
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+**Note:** If you set a `new_edits` property on the options object to `false`, you can post existing documents from other databases without having new revision IDs assigned to them. Normally, only the [replication algorithm](https://docs.couchdb.org/en/stable/replication/protocol.html?highlight=new_edits#upload-batch-of-changed-documents) needs to do this.
diff --git a/docs/version/latest/api/batch_fetch.html b/docs/version/latest/api/batch_fetch.html
new file mode 100644
index 0000000000..0e7e6749ae
--- /dev/null
+++ b/docs/version/latest/api/batch_fetch.html
@@ -0,0 +1,205 @@
+{% include anchor.html edit="true" title="Fetch a batch of documents" hash="batch_fetch" %}
+
+{% highlight js %}
+db.allDocs([options], [callback])
+{% endhighlight %}
+
+Fetch multiple documents, indexed and sorted by the `_id`. Deleted documents are only included if `options.keys` is specified.
+
+### Options
+
+All options default to `false` unless otherwise specified.
+
+* `options.include_docs`: Include the document itself in each row in the `doc` field. Otherwise by default you only get the `_id` and `_rev` properties.
+* `options.conflicts`: Include conflict information in the `_conflicts` field of a doc.
+* `options.attachments`: Include attachment data as base64-encoded string.
+* `options.binary`: Return attachment data as Blobs/Buffers, instead of as base64-encoded strings.
+* `options.startkey` & `options.endkey`: Get documents with IDs in a certain range (inclusive/inclusive).
+* `options.inclusive_end`: Include documents having an ID equal to the given `options.endkey`. Default: `true`.
+* `options.limit`: Maximum number of documents to return.
+* `options.skip`: Number of docs to skip before returning (warning: poor performance on IndexedDB/LevelDB!).
+* `options.descending`: Reverse the order of the output documents. Note that the order of `startkey` and `endkey` is reversed when `descending`:`true`.
+* `options.key`: Only return documents with IDs matching this string key.
+* `options.keys`: Array of string keys to fetch in a single shot.
+ - Neither `startkey` nor `endkey` can be specified with this option.
+ - The rows are returned in the same order as the supplied `keys` array.
+ - The row for a deleted document will have the revision ID of the deletion, and an extra key `"deleted":true` in the `value` property.
+ - The row for a nonexistent document will just contain an `"error"` property with the value `"not_found"`.
+ - For details, see the [CouchDB query options documentation](https://docs.couchdb.org/en/stable/api/ddoc/views.html#db-design-design-doc-view-view-name).
+* `options.update_seq`: Include an `update_seq` value indicating which sequence id of the underlying database the view reflects.
+
+**Notes:** For pagination, `options.limit` and `options.skip` are also available, but the same performance concerns as in CouchDB apply. Use the [startkey/endkey pattern](https://docs.couchdb.org/en/stable/couchapp/views/pagination.html) instead.
+
+#### Example Usage:
+
+{% include code/start.html id="all_docs" type="callback" %}
+{% highlight js %}
+db.allDocs({
+ include_docs: true,
+ attachments: true
+}, function(err, response) {
+ if (err) { return console.log(err); }
+ // handle result
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="all_docs" type="async" %}
+{% highlight js %}
+try {
+ const result = await db.allDocs({
+ include_docs: true,
+ attachments: true
+ });
+} catch (err) {
+ console.log(err);
+}
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="all_docs" type="promise" %}
+{% highlight js %}
+db.allDocs({
+ include_docs: true,
+ attachments: true
+}).then(function (result) {
+ // handle result
+}).catch(function (err) {
+ console.log(err);
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+#### Example Response:
+
+{% highlight js %}
+{
+ "offset": 0,
+ "total_rows": 1,
+ "rows": [{
+ "doc": {
+ "_id": "0B3358C1-BA4B-4186-8795-9024203EB7DD",
+ "_rev": "1-5782E71F1E4BF698FA3793D9D5A96393",
+ "title": "Sound and Vision",
+ "_attachments": {
+ "attachment/its-id": {
+ "content_type": "image/jpg",
+ "data": "R0lGODlhAQABAIAAAP7//wAAACH5BAAAAAAALAAAAAABAAEAAAICRAEAOw==",
+ "digest": "md5-57e396baedfe1a034590339082b9abce"
+ }
+ }
+ },
+ "id": "0B3358C1-BA4B-4186-8795-9024203EB7DD",
+ "key": "0B3358C1-BA4B-4186-8795-9024203EB7DD",
+ "value": {
+ "rev": "1-5782E71F1E4BF698FA3793D9D5A96393"
+ }
+ }]
+}
+{% endhighlight %}
+
+In the response, you have three things:
+
+* `total_rows` the total number of non-deleted documents in the database
+* `offset` the `skip` if provided, or in CouchDB the actual offset
+* `rows`: rows containing the documents, or just the `_id`/`_revs` if you didn't set `include_docs` to `true`.
+
+* You may optionally also have `update_seq` if you set `update_seq` to `true`
+
+You can use `startkey`/`endkey` to find all docs in a range:
+
+{% include code/start.html id="all_docs_2" type="callback" %}
+{% highlight js %}
+db.allDocs({
+ include_docs: true,
+ attachments: true,
+ startkey: 'bar',
+ endkey: 'quux'
+}, function(err, response) {
+ if (err) { return console.log(err); }
+ // handle result
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="all_docs_2" type="async" %}
+{% highlight js %}
+try {
+ const result = await db.allDocs({
+ include_docs: true,
+ attachments: true,
+ startkey: 'bar',
+ endkey: 'quux'
+ });
+} catch (err) {
+ console.log(err);
+}
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="all_docs_2" type="promise" %}
+{% highlight js %}
+db.allDocs({
+ include_docs: true,
+ attachments: true,
+ startkey: 'bar',
+ endkey: 'quux'
+}).then(function (result) {
+ // handle result
+}).catch(function (err) {
+ console.log(err);
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+This will return all docs with `_id`s between `'bar'` and `'quux'`.
+
+#### Prefix search
+
+You can do prefix search in `allDocs()` – i.e. "give me all the documents whose `_id`s start with `'foo'`" – by using the special high Unicode character `'\ufff0'`:
+
+{% include code/start.html id="all_docs_3" type="callback" %}
+{% highlight js %}
+db.allDocs({
+ include_docs: true,
+ attachments: true,
+ startkey: 'foo',
+ endkey: 'foo\ufff0'
+}, function(err, response) {
+ if (err) { return console.log(err); }
+ // handle result
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="all_docs_3" type="async" %}
+{% highlight js %}
+try {
+ const result = await db.allDocs({
+ include_docs: true,
+ attachments: true,
+ startkey: 'foo',
+ endkey: 'foo\ufff0'
+ });
+} catch (err) {
+ console.log(err);
+}
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="all_docs_3" type="promise" %}
+{% highlight js %}
+db.allDocs({
+ include_docs: true,
+ attachments: true,
+ startkey: 'foo',
+ endkey: 'foo\ufff0'
+}).then(function (result) {
+ // handle result
+}).catch(function (err) {
+ console.log(err);
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+This works because CouchDB/PouchDB `_id`s are sorted [lexicographically](https://docs.couchdb.org/en/stable/couchapp/views/collation.html).
diff --git a/docs/version/latest/api/bulk_get.html b/docs/version/latest/api/bulk_get.html
new file mode 100644
index 0000000000..a664a3e1b3
--- /dev/null
+++ b/docs/version/latest/api/bulk_get.html
@@ -0,0 +1,118 @@
+{% include anchor.html edit="true" title="Document bulk get" hash="bulk_get" %}
+
+{% highlight js %}
+db.bulkGet(options, [callback])
+{% endhighlight %}
+
+Given a set of document/revision IDs, returns the document bodies (and, optionally, attachment data) for each ID/revision pair specified.
+
+### Options
+
+* `options.docs`: An array of `id` and `rev` pairs representing the revisions to fetch.
+ - `id`: ID of the document to fetch.
+ - `rev`: Revision of the document to fetch. If this is not specified, all available revisions are fetched.
+ - `atts_since`: Optional and supported by the http adapter only. Includes attachments only since specified revisions. Doesn’t includes attachments for specified revisions.
+* `options.revs`: Each returned revision body will include its revision history as a `_revisions` property. Default is `false`.
+* `options.attachments`: Include attachment data in the response. Default is `false`, resulting in only stubs being returned.
+* `options.binary`: Return attachment data as Blobs/Buffers, instead of as base64-encoded strings. Default is `false`.
+
+
+
+#### Example Usage:
+
+{% include code/start.html id="bulkget1" type="callback" %}
+{% highlight js %}
+db.bulkGet({
+ docs: [
+ { id: "existing-doc", rev: "1-b2e54331db828310f3c772d6e042ac9c"},
+ { id: "foo", rev: "2-3a24009a9525bde9e4bfa8a99046b00d"},
+ { id: "bar", rev: "1-3a24009a9525bde9e4bfa8a99046b00d"}
+ ]
+}, function (err, result) {
+ if (err) { return console.log(err); }
+ // handle result
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="bulkget1" type="async" %}
+{% highlight js %}
+try {
+ const result = await db.bulkGet({
+ docs: [
+ { id: "doc-that-exists", rev: "1-967a00dff5e02add41819138abb3284d"},
+ { id: "doc-that-does-not-exist", rev: "1-3a24009a9525bde9e4bfa8a99046b00d"},
+ { id: "doc-that-exists", rev: "1-bad_rev"}
+ ]
+ });
+} catch (err) {
+ console.log(err);
+}
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="bulkget1" type="promise" %}
+{% highlight js %}
+db.bulkGet({
+ docs: [
+ { id: "doc-that-exists", rev: "1-967a00dff5e02add41819138abb3284d"},
+ { id: "doc-that-does-not-exist", rev: "1-3a24009a9525bde9e4bfa8a99046b00d"},
+ { id: "doc-that-exists", rev: "1-bad_rev"}
+ ]
+}).then(function (result) {
+ // handle result
+}).catch(function (err) {
+ console.log(err);
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+#### Example Response:
+{% highlight js %}
+{
+ "results": [
+ {
+ "docs": [
+ {
+ "ok": {
+ "_id": "doc-that-exists",
+ "_rev": "1-967a00dff5e02add41819138abb3284d",
+ "_revisions": {
+ "ids": [
+ "967a00dff5e02add41819138abb3284d"
+ ],
+ "start": 1
+ }
+ }
+ }],
+ "id": "doc-that-exists"
+ },
+ {
+ "docs": [
+ {
+ "error": {
+ "error": "not_found",
+ "id": "doc-that-does-not-exist",
+ "reason": "missing",
+ "rev": "undefined"
+ }
+ }],
+ "id": "doc-that-does-not-exist"
+ },
+ {
+ "docs": [
+ {
+ "error": {
+ "error": "not_found",
+ "id": "doc-that-exists",
+ "reason": "missing",
+ "rev": "1-badrev"
+ }
+ }
+ ],
+ "id": "doc-that-exists"
+ }]
+}
+{% endhighlight %}
+
+
diff --git a/docs/version/latest/api/changes.html b/docs/version/latest/api/changes.html
new file mode 100644
index 0000000000..6bfbf69079
--- /dev/null
+++ b/docs/version/latest/api/changes.html
@@ -0,0 +1,358 @@
+{% include anchor.html edit="true" title="Listen to database changes" hash="changes" %}
+
+{% highlight js %}
+db.changes(options)
+{% endhighlight %}
+
+A list of changes made to documents in the database, in the order they were made.
+It returns an object with the method `cancel()`, which you call if you don't want to listen to new changes anymore.
+
+It is an [event emitter][event emitter] and will emit a `'change'` event on each document change, a `'complete'` event when all the changes have been processed, and an `'error'` event when an error occurs. Calling `cancel()` will unsubscribe all event listeners automatically.
+
+### Options
+
+All options default to `false` unless otherwise specified.
+
+* `options.live`: Will emit change events for all future changes until cancelled.
+* `options.include_docs`: Include the associated document with each change.
+ * `options.conflicts`: Include conflicts.
+ * `options.attachments`: Include attachments.
+ - `options.binary`: Return attachment data as Blobs/Buffers, instead of as base64-encoded strings.
+* `options.descending`: Reverse the order of the output documents.
+* `options.since`: Start the results from the change immediately after the given sequence number. You can also pass `'now'` if you want only new changes (when `live` is `true`). Ignored if `descending` is true.
+* `options.limit`: Limit the number of results to this number.
+* `options.timeout`: Request timeout (in milliseconds), use `false` to disable.
+* `options.heartbeat`: For http adapter only, time in milliseconds for server to give a heartbeat to keep long connections open. Defaults to 10000 (10 seconds), use `false` to disable the default.
+
+**Filtering Options:**
+
+* `options.filter`: Reference a filter function from a design document to selectively get updates. To use a view function, pass `_view` here and provide a reference to the view function in `options.view`. See [filtered changes](#filtered-changes) for details.
+* `options.doc_ids`: Only show changes for docs with these ids (array of strings).
+* `options.query_params`: Object containing properties that are passed to the filter function, e.g. `{"foo:"bar"}`, where `"bar"` will be available in the filter function as `params.query.foo`. To access the `params`, define your filter function like `function (doc, params) {/* ... */}`.
+* `options.view`: Specify a view function (e.g. `'design_doc_name/view_name'` or `'view_name'` as shorthand for `'view_name/view_name'`) to act as a filter. Documents counted as "passed" for a view filter if a map function emits at least one record for them. **Note**: `options.filter` must be set to `'_view'` for this option to work.
+* `options.selector`: Filter using a query/pouchdb-find [selector](https://docs.couchdb.org/en/stable/api/database/find.html#find-selectors). **Note**: Cannot be used in combination with the filter option.
+
+**Advanced Options:**
+
+* `options.return_docs`: Defaults to `true` except when `options.live = true` then it defaults to `false`. Passing `false` prevents the changes feed from keeping all the documents in memory – in other words complete always has an empty results array, and the `change` event is the only way to get the event. Useful for large change sets where otherwise you would run out of memory.
+* `options.batch_size`: Only available for http databases, this configures how many changes to fetch at a time. Increasing this can reduce the number of requests made. Default is 25.
+* `options.style`: Specifies how many revisions are returned in the changes array. The default, `'main_only'`, will only return the current "winning" revision; `'all_docs'` will return all leaf revisions (including conflicts and deleted former conflicts). Most likely you won't need this unless you're writing a replicator.
+* `options.seq_interval`: Only available for http databases. Specifies that seq information only be generated every N changes. Larger values can improve changes throughput with CouchDB 2.0 and later. Note that `last_seq` is always populated regardless.
+
+#### Example Usage:
+
+{% highlight js %}
+const changes = db.changes({
+ since: 'now',
+ live: true,
+ include_docs: true
+}).on('change', function(change) {
+ // handle change
+}).on('complete', function(info) {
+ // changes() was canceled
+}).on('error', function (err) {
+ console.log(err);
+});
+
+changes.cancel(); // whenever you want to cancel
+{% endhighlight %}
+
+#### Example Response:
+{% highlight js %}
+{
+ "id":"somestuff",
+ "seq":21,
+ "changes":[{
+ "rev":"1-8e6e4c0beac3ec54b27d1df75c7183a8"
+ }],
+ "doc":{
+ "title":"Ch-Ch-Ch-Ch-Changes",
+ "_id":"someDocId",
+ "_rev":"1-8e6e4c0beac3ec54b27d1df75c7183a8"
+ }
+}
+{% endhighlight %}
+
+#### Change events
+
+* __`change`__ (`info`) - This event fires when a change has been found. `info` will contain details about the change, such as whether it was deleted and what the new `_rev` is. `info.doc` will contain the doc if you set `include_docs` to `true`. See below for an example response.
+* __`complete`__ (`info`) - This event fires when all changes have been read. In live changes, only cancelling the changes should trigger this event. `info.results` will contain the list of changes. See below for an example.
+* __`error`__ (`err`) - This event is fired when the changes feed is stopped due to an unrecoverable failure.
+
+#### Example response
+
+Example response in the `'change'` listener (using `{include_docs: true}`):
+
+{% highlight js %}
+{ id: 'doc1',
+ changes: [ { rev: '1-9152679630cc461b9477792d93b83eae' } ],
+ doc: {
+ _id: 'doc1',
+ _rev: '1-9152679630cc461b9477792d93b83eae'
+ },
+ seq: 1
+}
+{% endhighlight %}
+
+Example response in the `'change'` listener when a doc was deleted:
+
+{% highlight js %}
+{ id: 'doc2',
+ changes: [ { rev: '2-9b50a4b63008378e8d0718a9ad05c7af' } ],
+ doc: { _id: 'doc2',
+ _rev: '2-9b50a4b63008378e8d0718a9ad05c7af',
+ _deleted: true
+ },
+ deleted: true,
+ seq: 3
+}
+{% endhighlight %}
+
+Example response in the `'complete'` listener:
+
+{% highlight js %}
+{
+ "results": [
+ {
+ "id": "doc1",
+ "changes": [ { "rev": "1-9152679630cc461b9477792d93b83eae" } ],
+ "doc": {
+ "_id": "doc1",
+ "_rev": "1-9152679630cc461b9477792d93b83eae"
+ },
+ "seq": 1
+ },
+ {
+ "id": "doc2",
+ "changes": [ { "rev": "2-9b50a4b63008378e8d0718a9ad05c7af" } ],
+ "doc": {
+ "_id": "doc2",
+ "_rev": "2-9b50a4b63008378e8d0718a9ad05c7af",
+ "_deleted": true
+ },
+ "deleted": true,
+ "seq": 3
+ }
+ ],
+ "last_seq": 3
+}
+{% endhighlight %}
+
+`seq` and `last_seq` correspond to the overall sequence number of the entire database, and it's what is passed in when using `since` (except for the special `'now'`). It is the primary key for the changes feed, and is also used as a checkpointer by the replication algorithm.
+
+#### Single-shot
+
+If you don't specify `{live: true}`, then you can also use `changes()` in the standard
+callback/promise style, and it will be treated as a single-shot request, which
+returns a list of the changes (i.e. what the `'complete'` event emits):
+
+{% include code/start.html id="changes1" type="callback" %}
+{% highlight js %}
+db.changes({
+ limit: 10,
+ since: 0
+}, function (err, response) {
+ if (err) { return console.log(err); }
+ // handle result
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="changes1" type="async" %}
+{% highlight js %}
+try {
+ const result = await db.changes({
+ limit: 10,
+ since: 0
+ });
+} catch (err) {
+ console.log(err);
+}
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="changes1" type="promise" %}
+{% highlight js %}
+db.changes({
+ limit: 10,
+ since: 0
+}).then(function (result) {
+ // handle result
+}).catch(function (err) {
+ console.log(err);
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+#### Example Response:
+
+{% highlight js %}
+{
+ "results": [{
+ "id": "0B3358C1-BA4B-4186-8795-9024203EB7DD",
+ "seq": 1,
+ "changes": [{
+ "rev": "1-5782E71F1E4BF698FA3793D9D5A96393"
+ }]
+ }, {
+ "id": "mydoc",
+ "seq": 2,
+ "changes": [{
+ "rev": "1-A6157A5EA545C99B00FF904EEF05FD9F"
+ }]
+ }, {
+ "id": "otherdoc",
+ "seq": 3,
+ "changes": [{
+ "rev": "1-3753476B70A49EA4D8C9039E7B04254C"
+ }]
+ }, {
+ "id": "828124B9-3973-4AF3-9DFD-A94CE4544005",
+ "seq": 4,
+ "changes": [{
+ "rev": "1-A8BC08745E62E58830CA066D99E5F457"
+ }]
+ }],
+ "last_seq": 4
+}
+{% endhighlight %}
+
+When `live` is `false`, the returned object is also an event emitter as well as a promise,
+and will fire the `'complete'` event when the results are ready.
+
+Note that this `'complete'` event only fires when you aren't doing live changes.
+
+#### Filtered changes
+
+As with [replicate()](#replication), you can filter using:
+
+* an ad-hoc `filter` function
+* an array of `doc_ids`
+* a `filter` function inside of a design document
+* a `filter` function inside of a design document, with `query_params`
+* a `view` function inside of a design document
+
+If you are running `changes()` on a remote CouchDB, then the first method will run client-side, whereas the last four will filter on the server side. Therefore the last four should be preferred, especially if the database is large, because you want to send as few documents over the wire as possible.
+
+If you are running `changes()` on a local PouchDB, then obviously all five methods will run client-side. There are also no performance benefits to using any of the five, so can also just filter yourself, in your own `on('change')` handler. These methods are implemented in PouchDB purely for consistency with CouchDB.
+
+The named functions can either be specified with `'designdoc_id/function_name'` or (if both design doc id and function name are equal) as `'fname'` as shorthand for `'fname/fname'`.
+
+#### Filtering examples
+
+In these examples, we'll work with some mammals. Let's imagine our docs are:
+
+{% highlight js %}
+[
+ {_id: 'a', name: 'Kangaroo', type: 'marsupial'},
+ {_id: 'b', name: 'Koala', type: 'marsupial'},
+ {_id: 'c', name: 'Platypus', type: 'monotreme'}
+]
+{% endhighlight %}
+
+Here are 5 examples using the 5 different systems.
+
+**Example 1: Ad-hoc `filter` function**
+
+*Warning*: this runs client-side, if the database is remote.
+
+Filter by `type === 'marsupial'`:
+
+{% highlight js %}
+db.changes({
+ filter: function (doc) {
+ return doc.type === 'marsupial';
+ }
+});
+{% endhighlight %}
+
+**Example 2: Array of `doc_ids`**
+
+Filter documents with `_id`s `['a', 'c']`.
+
+{% highlight js %}
+db.changes({
+ doc_ids: ['a', 'c']
+});
+{% endhighlight %}
+
+**Example 3: `filter` function inside of a design document**
+
+First `put()` a design document:
+
+{% highlight js %}
+{
+ _id: '_design/mydesign',
+ filters: {
+ myfilter: function (doc) {
+ return doc.type === 'marsupial';
+ }.toString()
+ }
+}
+{% endhighlight %}
+
+Then filter by `type === 'marsupial'`:
+
+{% highlight js %}
+db.changes({
+ filter: 'mydesign/myfilter'
+});
+{% endhighlight %}
+
+**Example 4: `filter` function inside of a design document, with `query_params`**
+
+This is the most powerful way to filter, because it allows you to pass in arbitrary options to your filter function.
+
+First `put()` a design document:
+
+{% highlight js %}
+{
+ _id: '_design/myfilter',
+ filters: {
+ myfilter: function (doc, req) {
+ return doc.type === req.query.type;
+ }.toString()
+ }
+}
+{% endhighlight %}
+
+Then filter by `type === 'marsupial'`:
+
+{% highlight js %}
+db.changes({
+ filter: 'myfilter',
+ query_params: {type: 'marsupial'}
+});
+{% endhighlight %}
+
+Since both the design document and the filter function have the same name, we can shorten the function name to `'myfilter'`.
+
+**Example 5: `view` function inside of a design document**
+
+This doesn't really offer any advantages compared to the previous two methods, unless you are already using a `view` for map/reduce queries, and you want to reuse it.
+
+Any documents that `emit()` anything will be considered to have passed this filter method.
+
+First `put()` a design document:
+
+{% highlight js %}
+{
+ _id: '_design/mydesign',
+ views: {
+ myview: function (doc) {
+ if (doc.type === 'marsupial') {
+ emit(doc._id);
+ }
+ }.toString()
+ }
+}
+{% endhighlight %}
+
+Then filter by `type === 'marsupial'`:
+
+{% highlight js %}
+db.changes({
+ filter: '_view',
+ view: 'mydesign/myview'
+});
+{% endhighlight %}
diff --git a/docs/version/latest/api/close_database.html b/docs/version/latest/api/close_database.html
new file mode 100644
index 0000000000..3e82f1563e
--- /dev/null
+++ b/docs/version/latest/api/close_database.html
@@ -0,0 +1,33 @@
+{% include anchor.html edit="true" title="Close a database" hash="close_database"%}
+
+{% highlight js %}
+db.close([callback])
+{% endhighlight %}
+
+Close the database, this closes any open connection to the underlying storage and frees memory (event listeners) the database may be using.
+
+#### Example Usage
+
+{% include code/start.html id="close_db" type="callback" %}
+{% highlight js %}
+db.close(function () {
+ // success
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="close_db" type="async" %}
+
+{% highlight js %}
+await db.close();
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="close_db" type="promise" %}
+
+{% highlight js %}
+db.close().then(function () {
+ // success
+});
+{% endhighlight %}
+{% include code/end.html %}
diff --git a/docs/version/latest/api/compaction.html b/docs/version/latest/api/compaction.html
new file mode 100644
index 0000000000..c936375d54
--- /dev/null
+++ b/docs/version/latest/api/compaction.html
@@ -0,0 +1,49 @@
+{% include anchor.html edit="true" title="Compact the database" hash="compaction" %}
+
+{% highlight js %}
+db.compact([options], [callback])
+{% endhighlight %}
+
+Triggers a compaction operation in the local or remote database. This reduces the database's size by removing unused and old data, namely non-leaf revisions and attachments that are no longer referenced by those revisions. Note that this is a separate operation from [`viewCleanup()`](#view_cleanup).
+
+For remote databases, PouchDB checks the compaction status at regular intervals and fires the callback (or resolves the promise) upon completion. Consult the [compaction section of CouchDB's maintenance documentation](http://couchdb.readthedocs.org/en/latest/maintenance/compaction.html) for more details.
+
+Also see [auto-compaction](#create_database), which runs compaction automatically (local databases only).
+
+* `options.interval`: Number of milliseconds to wait before asking again if compaction is already done. Defaults to 200. (Only applies to remote databases.)
+
+#### Example Usage:
+
+{% include code/start.html id="compact" type="callback" %}
+{% highlight js %}
+db.compact(function (err, result) {
+ if (err) { return console.log(err); }
+ // handle result
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="compact" type="async" %}
+{% highlight js %}
+try {
+ const result = await db.compact();
+} catch (err) {
+ console.log(err);
+}
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="compact" type="promise" %}
+{% highlight js %}
+db.compact().then(function (result) {
+ // handle result
+}).catch(function (err) {
+ console.log(err);
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+#### Example Response:
+{% highlight js %}
+{ "ok" : "true" }
+{% endhighlight %}
diff --git a/docs/version/latest/api/create_database.html b/docs/version/latest/api/create_database.html
new file mode 100644
index 0000000000..d2792ca0fc
--- /dev/null
+++ b/docs/version/latest/api/create_database.html
@@ -0,0 +1,64 @@
+{% include anchor.html edit="true" title="Create a database" hash="create_database" %}
+
+{% highlight js %}
+new PouchDB([name], [options])
+{% endhighlight %}
+
+This method creates a database or opens an existing one. If you use a URL like `'http://domain.com/dbname'`, then PouchDB will work as a client to an online CouchDB instance. Otherwise it will create a local database using [whatever backend is present](/adapters.html).
+
+### Options
+
+* `name`: You can omit the `name` argument and specify it via `options` instead. Note that the name is required.
+
+**Options for local databases:**
+
+* `auto_compaction`: This turns on auto compaction, which means `compact()` is called after every change to the database. Defaults to `false`.
+* `adapter`: One of `'indexeddb'`, `'idb'`, `'leveldb'`, `'nodesqlite'`, or `'http'`.
+* `revs_limit`: Specify how many old revisions we keep track (not a copy) of. Specifying a low value means Pouch may not be able to figure out whether a new revision received via replication is related to any it currently has which could result in a conflict. Defaults to `1000`.
+* `deterministic_revs`: Use a md5 hash to create a deterministic revision number for documents. Setting it to false will mean that the revision number will be a random UUID. Defaults to true.
+* `view_update_changes_batch_size`: Specify how many change records will be consumed at a time when rebuilding view indexes when the `query()` method is used. Defaults to 50.
+* `view_adapter`: Specify a different adapter to store the view index data.
+* `purged_infos_limit`: Specify how many purged revisions we keep track of. Specifying a low value can result in Pouch not delegating older purges down to views. Defaults to `1000`.
+
+**Options for remote databases:**
+
+* `fetch(url, opts)`: Intercept or override the HTTP request, you can add or modify any headers or options relating to the http request then return a new fetch Promise.
+* `auth.username` + `auth.password`: You can specify HTTP auth parameters either by using a database with a name in the form `http://user:pass@host/name` or via the `auth.username` + `auth.password` options.
+* `skip_setup`: Initially PouchDB checks if the database exists, and tries to create it, if it does not exist yet. Set this to `true` to skip this setup.
+
+**Notes:**
+
+1. In IndexedDB PouchDB will use `_pouch_` to prefix the internal database names. Do not manually create databases with the same prefix.
+2. When acting as a client on Node, any other options given will be passed to [request][].
+3. When using the `'leveldb'` adapter (the default on Node), any other options given will be passed to [levelup][].
+4. If you are using the jwt auth handler, please use the fetch option to add the required headers and handle connected logic like token refreshes.
+
+[request]: https://github.com/mikeal/request
+[levelup]: https://github.com/rvagg/node-levelup
+[levelup_options]: https://github.com/rvagg/node-levelup/#options
+
+#### Example Usage:
+{% highlight js %}
+const db = new PouchDB('dbname');
+// or
+const db = new PouchDB('http://localhost:5984/dbname');
+{% endhighlight %}
+
+Create an in-memory Pouch (must install `pouchdb-adapter-memory` first):
+
+{% highlight js %}
+const db = new PouchDB('dbname', {adapter: 'memory'});
+{% endhighlight %}
+
+Create a remote PouchDB with special fetch options:
+
+{% highlight js %}
+const db = new PouchDB('http://example.com/dbname', {
+ fetch: function (url, opts) {
+ opts.headers.set('X-Some-Special-Header', 'foo');
+ return PouchDB.fetch(url, opts);
+ }
+});
+{% endhighlight %}
+
+For more info, check out [adapters](/adapters.html).
diff --git a/docs/version/latest/api/create_document.html b/docs/version/latest/api/create_document.html
new file mode 100644
index 0000000000..6a377ead31
--- /dev/null
+++ b/docs/version/latest/api/create_document.html
@@ -0,0 +1,174 @@
+{% include anchor.html edit="true" title="Create/update a document" hash="create_document" %}
+
+### Using db.put()
+{% highlight js %}
+db.put(doc, [options], [callback])
+{% endhighlight %}
+
+Create a new document or update an existing document. If the document already exists, you must specify its revision `_rev`, otherwise a conflict will occur.
+
+If you want to update an existing document even if there's conflict, you should specify the base revision `_rev` and use `force=true` option, then a new conflict revision will be created.
+
+`doc` must be a "pure JSON object", i.e. a collection of name/value pairs. If you try to store non-JSON data (for instance `Date` objects) you may see [inconsistent results]({{ site.baseurl }}/errors.html#could_not_be_cloned).
+
+#### Example Usage:
+
+Create a new doc with an `_id` of `'mydoc'`:
+
+{% include code/start.html id="newDoc" type="callback" %}
+{% highlight js %}
+db.put({
+ _id: 'mydoc',
+ title: 'Heroes'
+}, function(err, response) {
+ if (err) { return console.log(err); }
+ // handle response
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="newDoc" type="async" %}
+{% highlight js %}
+try {
+ const response = await db.put({
+ _id: 'mydoc',
+ title: 'Heroes'
+ });
+} catch (err) {
+ console.log(err);
+}
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="newDoc" type="promise" %}
+{% highlight js %}
+db.put({
+ _id: 'mydoc',
+ title: 'Heroes'
+}).then(function (response) {
+ // handle response
+}).catch(function (err) {
+ console.log(err);
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+You can update an existing doc using `_rev`:
+
+{% include code/start.html id="updateDoc" type="callback" %}
+{% highlight js %}
+db.get('mydoc', function(err, doc) {
+ if (err) { return console.log(err); }
+ db.put({
+ _id: 'mydoc',
+ _rev: doc._rev,
+ title: "Let's Dance"
+ }, function(err, response) {
+ if (err) { return console.log(err); }
+ // handle response
+ });
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="updateDoc" type="async" %}
+{% highlight js %}
+try {
+ const doc = await db.get('mydoc');
+ const response = await db.put({
+ _id: 'mydoc',
+ _rev: doc._rev,
+ title: "Let's Dance"
+ });
+} catch (err) {
+ console.log(err);
+}
+{% endhighlight %}
+{% include code/end.html %}
+
+
+{% include code/start.html id="updateDoc" type="promise" %}
+{% highlight js %}
+db.get('mydoc').then(function(doc) {
+ return db.put({
+ _id: 'mydoc',
+ _rev: doc._rev,
+ title: "Let's Dance"
+ });
+}).then(function(response) {
+ // handle response
+}).catch(function (err) {
+ console.log(err);
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+#### Example Response:
+{% highlight js %}
+{
+ "ok": true,
+ "id": "mydoc",
+ "rev": "1-A6157A5EA545C99B00FF904EEF05FD9F"
+}
+{% endhighlight %}
+
+The response contains the `id` of the document, the new `rev`, and an `ok` to reassure
+you that everything is okay.
+
+### Using db.post()
+
+{% highlight js %}
+db.post(doc, [options], [callback])
+{% endhighlight %}
+
+Create a new document and let PouchDB auto-generate an `_id` for it.
+
+#### Example Usage:
+
+{% include code/start.html id="post_doc" type="callback" %}
+{% highlight js %}
+db.post({
+ title: 'Ziggy Stardust'
+}, function (err, response) {
+ if (err) { return console.log(err); }
+ // handle response
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="post_doc" type="async" %}
+{% highlight js %}
+try {
+ const response = await db.post({
+ title: 'Ziggy Stardust'
+ });
+} catch (err) {
+ console.log(err);
+}
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="post_doc" type="promise" %}
+{% highlight js %}
+db.post({
+ title: 'Ziggy Stardust'
+}).then(function (response) {
+ // handle response
+}).catch(function (err) {
+ console.log(err);
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+#### Example Response:
+{% highlight js %}
+{
+ "ok" : true,
+ "id" : "8A2C3761-FFD5-4770-9B8C-38C33CED300A",
+ "rev" : "1-d3a8e0e5aa7c8fff0c376dac2d8a4007"
+}
+{% endhighlight %}
+
+**Put vs. post**: The basic rule of thumb is: `put()` new documents with an `_id`, `post()` new documents without an `_id`.
+
+You should also prefer `put()` to `post()`, because when you `post()`, you are missing an opportunity to use `allDocs()` to sort documents by `_id` (because your `_id`s are random). For more info, read the [PouchDB pro tips](/2014/06/17/12-pro-tips-for-better-code-with-pouchdb.html).
diff --git a/docs/version/latest/api/create_index.html b/docs/version/latest/api/create_index.html
new file mode 100644
index 0000000000..38b26a61f4
--- /dev/null
+++ b/docs/version/latest/api/create_index.html
@@ -0,0 +1,302 @@
+{% include anchor.html edit="true" title="Create index" hash="create_index" %}
+
+{% highlight js %}
+db.createIndex(index [, callback])
+{% endhighlight %}
+
+Create an index if it doesn't exist, or do nothing if it already exists.
+
+{% include alert/start.html variant="info"%}
+{% markdown %}
+
+**pouchdb-find plugin needed:** This API requires the `pouchdb-find` plugin. See
+[Mango queries](/guides/mango-queries.html) for installation instructions.
+
+{% endmarkdown %}
+{% include alert/end.html%}
+
+#### Example Usage:
+
+{% include code/start.html id="create_idx" type="callback" %}
+{% highlight js %}
+db.createIndex({
+ index: {
+ fields: ['foo']
+ }
+}, function (err, result) {
+ if (err) { return console.log(err); }
+ // handle result
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="create_idx" type="async" %}
+{% highlight js %}
+try {
+ const result = await db.createIndex({
+ index: {
+ fields: ['foo']
+ }
+ });
+} catch (err) {
+ console.log(err);
+}
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="create_idx" type="promise" %}
+{% highlight js %}
+db.createIndex({
+ index: {
+ fields: ['foo']
+ }
+}).then(function (result) {
+ // handle result
+}).catch(function (err) {
+ console.log(err);
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+#### Example Response:
+
+If the index was created, you'll see:
+
+{% highlight js %}
+{ "result": "created" }
+{% endhighlight %}
+
+Or if the index already exists:
+
+{% highlight js %}
+{ "result": "exists" }
+{% endhighlight %}
+
+You can also create an index on multiple fields:
+
+{% include code/start.html id="create_idx2" type="callback" %}
+{% highlight js %}
+db.createIndex({
+ index: {
+ fields: ['foo', 'bar', 'baz']
+ }
+}, function (err, result) {
+ if (err) { return console.log(err); }
+ // handle result
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="create_idx2" type="async" %}
+{% highlight js %}
+try {
+ const result = await db.createIndex({
+ index: {
+ fields: ['foo', 'bar', 'baz']
+ }
+ });
+} catch (err) {
+ console.log(err);
+}
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="create_idx2" type="promise" %}
+{% highlight js %}
+db.createIndex({
+ index: {
+ fields: ['foo', 'bar', 'baz']
+ }
+}).then(function (result) {
+ // handle result
+}).catch(function (err) {
+ console.log(err);
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+Or an index on deep fields:
+
+{% include code/start.html id="create_idx3" type="callback" %}
+{% highlight js %}
+db.createIndex({
+ index: {
+ fields: ['person.address.zipcode']
+ }
+}, function (err, result) {
+ if (err) { return console.log(err); }
+ // handle result
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="create_idx3" type="async" %}
+{% highlight js %}
+try {
+ const result = await db.createIndex({
+ index: {
+ fields: ['person.address.zipcode']
+ }
+ });
+} catch (err) {
+ console.log(err);
+}
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="create_idx3" type="promise" %}
+{% highlight js %}
+db.createIndex({
+ index: {
+ fields: ['person.address.zipcode']
+ }
+}).then(function (result) {
+ // handle result
+}).catch(function (err) {
+ console.log(err);
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+You can also specify additional options, if you want more control over how your index is created:
+
+{% include code/start.html id="create_idx4" type="callback" %}
+{% highlight js %}
+db.createIndex({
+ index: {
+ fields: ['foo', 'bar'],
+ name: 'myindex',
+ ddoc: 'mydesigndoc',
+ type: 'json',
+ }
+}, function (err, result) {
+ if (err) { return console.log(err); }
+ // handle result
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="create_idx4" type="async" %}
+{% highlight js %}
+try {
+ const result = await db.createIndex({
+ index: {
+ fields: ['foo', 'bar'],
+ name: 'myindex',
+ ddoc: 'mydesigndoc',
+ type: 'json',
+ }
+ });
+} catch (err) {
+ console.log(err);
+}
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="create_idx4" type="promise" %}
+{% highlight js %}
+db.createIndex({
+ index: {
+ fields: ['foo', 'bar'],
+ name: 'myindex',
+ ddoc: 'mydesigndoc',
+ type: 'json',
+ }
+}).then(function (result) {
+ // handle result
+}).catch(function (err) {
+ console.log(err);
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+If you want to index a subset of the documents in the database, you can use `partial_filter_selector`. This has the same syntax as the selector you'd pass to `find()`, and only documents matching the selector will be included in the index.
+
+{% include alert/start.html variant="warning"%}
+{% markdown %}
+
+**When using _pouchdb-adapter-indexeddb:_** On the `indexeddb`-adapter, indexes with
+a`partial_filter_selector` will fall back to using [map-reduce](#query_database),
+instead of the native indexes, negating all index performance gains from the
+`indexeddb`-adapter over the `idb`-adapter.
+
+{% endmarkdown %}
+{% include alert/end.html%}
+
+{% include code/start.html id="create_idx5" type="callback" %}
+{% highlight js %}
+db.createIndex({
+ index: {
+ fields: ['year', 'title'],
+ partial_filter_selector: {
+ year: { $gt: 2010 }
+ }
+ }
+}, function (err, result) {
+ if (err) { return console.log(err); }
+ // handle result
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="create_idx5" type="async" %}
+{% highlight js %}
+try {
+ const result = db.createIndex({
+ index: {
+ fields: ['year', 'title'],
+ partial_filter_selector: {
+ year: { $gt: 2010 }
+ }
+ }
+ });
+} catch (err) {
+ console.log(err);
+}
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="create_idx5" type="promise" %}
+{% highlight js %}
+db.createIndex({
+ index: {
+ fields: ['year', 'title'],
+ partial_filter_selector: {
+ year: { $gt: 2010 }
+ }
+ }
+}).then(function (result) {
+ // handle result
+}).catch(function (err) {
+ console.log(err);
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+### Options
+
+* `fields`: a list of fields to index
+* `name` (optional): name of the index, auto-generated if you don't include it
+* `ddoc` (optional): design document name (i.e. the part after `'_design/'`), auto-generated if you don't include it
+* `type` (optional): only supports `'json'`, which is also the default
+* `partial_filter_selector` (optional): a _selector_ used to filter the set of documents included in the index
+
+When a PouchDB instance updates an index, it emits `indexing` events that include information about the progress of the index update task.
+
+{% highlight js %}
+const db = new PouchDB('my-docs');
+
+db.on('indexing', function (event) {
+ // called when indexes are updated
+});
+{% endhighlight %}
+
+The `event` object contains the following fields:
+
+* `view`: the name of the view that's being updated
+* `indexed_docs`: the total number of document updates processed during the current run
+
+When an index is updated this event is emitted once at the start of the update, with `indexed_docs` equal to `0`. It will then emit further events as batches of changes are processed, and those events will also include these fields:
+
+* `last_seq`: the `seq` value of the last event from the database change feed that's been processed
+* `results_count`: the number of changes in the most recent batch of changes
diff --git a/docs/version/latest/api/database_information.html b/docs/version/latest/api/database_information.html
new file mode 100644
index 0000000000..e2cb79ff5f
--- /dev/null
+++ b/docs/version/latest/api/database_information.html
@@ -0,0 +1,59 @@
+{% include anchor.html edit="true" title="Get database information" hash="database_information" %}
+
+{% highlight js %}
+db.info([callback])
+{% endhighlight %}
+
+Get information about a database.
+
+#### Example Usage:
+
+{% include code/start.html id="dbinfo" type="callback" %}
+{% highlight js %}
+db.info(function(err, info) {
+ if (err) { return console.log(err); }
+ // handle result
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="dbinfo" type="async" %}
+{% highlight js %}
+try {
+ const result = await db.info();
+} catch (err) {
+ console.log(err);
+}
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="dbinfo" type="promise" %}
+{% highlight js %}
+db.info().then(function (result) {
+ // handle result
+}).catch(function (err) {
+ console.log(err);
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+#### Example Response:
+{% highlight js %}
+{
+ "db_name": "test",
+ "doc_count": 4,
+ "update_seq": 5
+}
+{% endhighlight %}
+
+**Response object:**
+
+* `db_name` is the name of the database you gave when you called `new PouchDB()`, and also the unique identifier for the database.
+* `doc_count` is the total number of non-deleted documents in the database.
+* `update_seq` is the sequence number of the database. It starts at 0 and gets incremented every time a document is added or modified.
+
+There are also some details you can use for debugging. These are unofficial and may change at any time:
+
+* `adapter`: The name of the adapter being used (idb, leveldb, ...).
+* `idb_attachment_format`: (IndexedDB) either `'base64'` or `'binary'`, depending on whether the browser [supports binary blobs](/faq.html#data_types).
+* `backend_adapter`: (Node.JS) the backend *DOWN adapter being used (MemDOWN, RiakDOWN, ...).
diff --git a/docs/version/latest/api/defaults.html b/docs/version/latest/api/defaults.html
new file mode 100644
index 0000000000..de2b22cab5
--- /dev/null
+++ b/docs/version/latest/api/defaults.html
@@ -0,0 +1,42 @@
+{% include anchor.html edit="true" title="Default settings" hash="defaults" %}
+
+If you find yourself using the same constructor options repeatedly,
+you can simplify your code with `PouchDB.defaults()`:
+
+{% highlight js %}
+PouchDB.defaults({
+ option1: 'foo',
+ option2: 'value'
+});
+{% endhighlight %}
+
+The returned object is a constructor function that works the same as `PouchDB`, except that whenever you invoke it (e.g. with `new`), the given options will be passed in by default.
+
+#### Example Usage:
+{% highlight js %}
+const MyMemPouch = PouchDB.defaults({
+ adapter: 'memory'
+});
+// In-memory PouchDB
+const myMemPouch = new MyMemPouch('dbname');
+
+const MyPrefixedPouch = PouchDB.defaults({
+ prefix: '/path/to/my/db/'
+});
+// db will be named '/path/to/my/db/dbname', useful for LevelDB
+const myPrefixedPouch = new MyPrefixedPouch('dbname');
+
+const HTTPPouch = PouchDB.defaults({
+ prefix: 'http://example.org'
+});
+
+// db will be located at 'http://example.org/dbname'
+const myHttpPouch = new HTTPPouch('dbname');
+{% endhighlight %}
+
+Note the special constructor option `prefix`, which appends a prefix to the database name
+and can be helpful for URL-based or file-based LevelDOWN path names.
+
+All [constructor options](#create_database) are supported. Default options can still be overriden individually.
+
+
diff --git a/docs/version/latest/api/delete_attachment.html b/docs/version/latest/api/delete_attachment.html
new file mode 100644
index 0000000000..bceff6a70c
--- /dev/null
+++ b/docs/version/latest/api/delete_attachment.html
@@ -0,0 +1,52 @@
+{% include anchor.html edit="true" title="Delete an attachment" hash="delete_attachment" %}
+
+{% highlight js %}
+db.removeAttachment(docId, attachmentId, rev, [callback])
+{% endhighlight %}
+
+Delete an attachment from a doc. You must supply the `rev` of the existing doc.
+
+#### Example Usage:
+
+{% include code/start.html id="delete_att" type="callback" %}
+{% highlight js %}
+const rev = '1-068E73F5B44FEC987B51354DFC772891';
+db.removeAttachment('doc', 'att.txt', rev, function(err, res) {
+ if (err) { return console.log(err); }
+ // handle result
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="delete_att" type="async" %}
+{% highlight js %}
+try {
+ const rev = '1-068E73F5B44FEC987B51354DFC772891';
+ const result = await db.removeAttachment('doc', 'att.txt', rev);
+} catch (err) {
+ console.log(err);
+}
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="delete_att" type="promise" %}
+{% highlight js %}
+const rev = '1-068E73F5B44FEC987B51354DFC772891';
+db.removeAttachment('doc', 'att.txt', rev).then(function (result) {
+ // handle result
+}).catch(function (err) {
+ console.log(err);
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+#### Example Response:
+
+{% highlight js %}
+{
+ "ok": true,
+ "rev": "2-1F983211AB87EFCCC980974DFC27382F"
+}
+{% endhighlight %}
+
+
diff --git a/docs/version/latest/api/delete_database.html b/docs/version/latest/api/delete_database.html
new file mode 100644
index 0000000000..69cca3cad8
--- /dev/null
+++ b/docs/version/latest/api/delete_database.html
@@ -0,0 +1,50 @@
+{% include anchor.html edit="true" title="Delete a database" hash="delete_database"%}
+
+{% highlight js %}
+db.destroy([options], [callback])
+{% endhighlight %}
+
+Delete the database. Note that this has no impact on other replicated databases.
+
+#### Example Usage
+
+{% include code/start.html id="destroy_db" type="callback" %}
+{% highlight js %}
+db.destroy(function (err, response) {
+ if (err) {
+ return console.log(err);
+ } else {
+ // success
+ }
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="destroy_db" type="async" %}
+
+{% highlight js %}
+try {
+ await db.destroy();
+} catch (err) {
+ console.log(err);
+}
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="destroy_db" type="promise" %}
+
+{% highlight js %}
+db.destroy().then(function (response) {
+ // success
+}).catch(function (err) {
+ console.log(err);
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+#### Example Response:
+{% highlight js %}
+{
+ "ok" : true
+}
+{% endhighlight %}
diff --git a/docs/version/latest/api/delete_document.html b/docs/version/latest/api/delete_document.html
new file mode 100644
index 0000000000..c6e11c7c15
--- /dev/null
+++ b/docs/version/latest/api/delete_document.html
@@ -0,0 +1,140 @@
+{% include anchor.html edit="true" title="Delete a document" hash="delete_document"%}
+
+{% highlight js %}
+db.remove(doc, [options], [callback])
+{% endhighlight %}
+
+Or:
+
+{% highlight js %}
+db.remove(docId, docRev, [options], [callback])
+{% endhighlight %}
+
+
+Deletes the document. `doc` is required to be a document with at least an `_id` and a `_rev` property. Sending the full document will work as well.
+
+See [filtered replication]({{ site.baseurl }}/api.html#filtered-replication) for why you might want to use `put()` with `{_deleted: true}` instead.
+
+#### Example Usage:
+
+{% include code/start.html id="delete_doc" type="callback" %}
+{% highlight js %}
+db.get('mydoc', function(err, doc) {
+ if (err) { return console.log(err); }
+ db.remove(doc, function(err, response) {
+ if (err) { return console.log(err); }
+ // handle response
+ });
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="delete_doc" type="async" %}
+{% highlight js %}
+try {
+ const doc = await db.get('mydoc');
+ const response = await db.remove(doc);
+} catch (err) {
+ console.log(err);
+}
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="delete_doc" type="promise" %}
+{% highlight js %}
+db.get('mydoc').then(function(doc) {
+ return db.remove(doc);
+}).then(function (result) {
+ // handle result
+}).catch(function (err) {
+ console.log(err);
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+#### Example Response:
+
+{% highlight js %}
+{
+ "ok": true,
+ "id": "mydoc",
+ "rev": "2-9AF304BE281790604D1D8A4B0F4C9ADB"
+}
+{% endhighlight %}
+
+You can also delete a document by just providing an `id` and `rev`:
+
+{% include code/start.html id="delete_doc3" type="callback" %}
+{% highlight js %}
+db.get('mydoc', function(err, doc) {
+ if (err) { return console.log(err); }
+ db.remove(doc._id, doc._rev, function(err, response) {
+ if (err) { return console.log(err); }
+ // handle response
+ });
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="delete_doc3" type="async" %}
+{% highlight js %}
+try {
+ const doc = await db.get('mydoc');
+ const response = await db.remove(doc._id, doc._rev);
+} catch (err) {
+ console.log(err);
+}
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="delete_doc3" type="promise" %}
+{% highlight js %}
+db.get('mydoc').then(function(doc) {
+ return db.remove(doc._id, doc._rev);
+}).then(function (result) {
+ // handle result
+}).catch(function (err) {
+ console.log(err);
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+You can also delete a document by using `put()` with `{_deleted: true}`:
+
+{% include code/start.html id="delete_doc2" type="callback" %}
+{% highlight js %}
+db.get('mydoc', function(err, doc) {
+ if (err) { return console.log(err); }
+ doc._deleted = true;
+ db.put(doc, function(err, response) {
+ if (err) { return console.log(err); }
+ // handle response
+ });
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="delete_doc2" type="async" %}
+{% highlight js %}
+try {
+ const doc = await db.get('mydoc');
+ doc._deleted = true;
+ const result = await db.put(doc);
+} catch (err) {
+ console.log(err);
+}
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="delete_doc2" type="promise" %}
+{% highlight js %}
+db.get('mydoc').then(function(doc) {
+ doc._deleted = true;
+ return db.put(doc);
+}).then(function (result) {
+ // handle result
+}).catch(function (err) {
+ console.log(err);
+});
+{% endhighlight %}
+{% include code/end.html %}
diff --git a/docs/version/latest/api/delete_index.html b/docs/version/latest/api/delete_index.html
new file mode 100644
index 0000000000..733bd743c1
--- /dev/null
+++ b/docs/version/latest/api/delete_index.html
@@ -0,0 +1,129 @@
+{% include anchor.html edit="true" title="Delete index" hash="delete_index" %}
+
+{% highlight js %}
+db.deleteIndex(index [, callback])
+{% endhighlight %}
+
+Delete an index, remove any orphaned design documents, and clean up any leftover data on disk.
+
+{% include alert/start.html variant="info"%}
+{% markdown %}
+
+**pouchdb-find plugin needed:** This API requires the `pouchdb-find` plugin. See
+[Mango queries](/guides/mango-queries.html) for installation instructions.
+
+{% endmarkdown %}
+{% include alert/end.html%}
+
+
+#### Example Usage:
+
+{% include code/start.html id="delete_idx" type="callback" %}
+{% highlight js %}
+db.deleteIndex({
+ "ddoc": "_design/idx-0f3a6f73110868266fa5c688caf8acd3",
+ "name": "idx-0f3a6f73110868266fa5c688caf8acd3",
+ "type": "json",
+ "def": {
+ "fields": [
+ { "foo": "asc" },
+ { "bar": "asc" }
+ ]
+ }
+}, function (err, result) {
+ if (err) { return console.log(err); }
+ // handle result
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="delete_idx" type="async" %}
+{% highlight js %}
+try {
+ const result = await db.deleteIndex({
+ "ddoc": "_design/idx-0f3a6f73110868266fa5c688caf8acd3",
+ "name": "idx-0f3a6f73110868266fa5c688caf8acd3",
+ "type": "json",
+ "def": {
+ "fields": [
+ { "foo": "asc" },
+ { "bar": "asc" }
+ ]
+ }
+ });
+} catch (err) {
+ console.log(err);
+}
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="delete_idx" type="promise" %}
+{% highlight js %}
+db.deleteIndex({
+ "ddoc": "_design/idx-0f3a6f73110868266fa5c688caf8acd3",
+ "name": "idx-0f3a6f73110868266fa5c688caf8acd3",
+ "type": "json",
+ "def": {
+ "fields": [
+ { "foo": "asc" },
+ { "bar": "asc" }
+ ]
+ }
+}).then(function (result) {
+ // handle result
+}).catch(function (err) {
+ console.log(err);
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+#### Example Response:
+
+{% highlight js %}
+{ "ok": true }
+{% endhighlight %}
+
+Note that the easiest way to do this is to locate the index you want to delete using [`getIndexes()`](#list_indexes).
+For instance, here is how you would delete the second index from that list (which should be the
+one after the built-in `_all_docs` index):
+
+{% include code/start.html id="delete_idx2" type="callback" %}
+{% highlight js %}
+db.getIndexes(function (err, indexesResult) {
+ if (err) { return console.log(err); }
+ db.deleteIndex(indexesResult.indexes[1], function (err, result) {
+ if (err) { return console.log(err); }
+ // handle result
+ });
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="delete_idx2" type="async" %}
+{% highlight js %}
+try {
+ const indexesResult = await db.getIndexes();
+ const result = await db.deleteIndex(indexesResult.indexes[1]);
+} catch (err) {
+ console.log(err);
+}
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="delete_idx2" type="promise" %}
+{% highlight js %}
+db.getIndexes().then(function (indexesResult) {
+ return db.deleteIndex(indexesResult.indexes[1]);
+}).then(function (result) {
+ // handle result
+}).catch(function (err) {
+ console.log(err);
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+**Notes:**
+
+* You don't need to provide a `_rev` when deleting an index.
+* The associated design doc is automatically deleted, assuming it only contains one index.
+* There is no need to call [`viewCleanup`](#view_cleanup) to clean up any leftover data. `deleteIndex()` does this automatically for you.
diff --git a/docs/version/latest/api/events.html b/docs/version/latest/api/events.html
new file mode 100644
index 0000000000..1e7563e305
--- /dev/null
+++ b/docs/version/latest/api/events.html
@@ -0,0 +1,14 @@
+{% include anchor.html edit="true" title="Events" hash="events" %}
+
+PouchDB is an [event emitter][event emitter] and will emit a `'created'` event when a database is created. A `'destroyed'` event is emitted when a database is destroyed.
+
+{% highlight js %}
+PouchDB.on('created', function (dbName) {
+ // called whenever a db is created.
+});
+PouchDB.on('destroyed', function (dbName) {
+ // called whenever a db is destroyed.
+});
+{% endhighlight %}
+
+
diff --git a/docs/version/latest/api/explain_index.html b/docs/version/latest/api/explain_index.html
new file mode 100644
index 0000000000..e79cd50e2c
--- /dev/null
+++ b/docs/version/latest/api/explain_index.html
@@ -0,0 +1,135 @@
+{% include anchor.html edit="true" title="Explain index" hash="explain_index" %}
+
+{% highlight js %}
+db.explain(request [, callback])
+{% endhighlight %}
+
+Explain the query plan for a given query
+
+{% include alert/start.html variant="info"%}
+{% markdown %}
+
+**pouchdb-find plugin needed:** This API requires the `pouchdb-find` plugin. See
+[Mango queries](/guides/mango-queries.html) for installation instructions.
+
+{% endmarkdown %}
+{% include alert/end.html%}
+
+#### Example Usage:
+
+{% include code/start.html id="explain_idx" type="callback" %}
+{% highlight js %}
+db.explain({
+ selector: {
+ name: 'Mario',
+ series: "mario"
+ },
+ fields: ['_id', 'name'],
+ sort: ['name']
+}, function (err, explanation) {
+ if (err) { return console.log(err); }
+ // view explanation
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="explain_idx" type="async" %}
+{% highlight js %}
+try {
+ const explanation = await db.explain({
+ selector: {
+ name: 'Mario',
+ series: "mario"
+ },
+ fields: ['_id', 'name'],
+ sort: ['name']
+ });
+} catch (err) {
+ console.log(err);
+}
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="explain_idx" type="promise" %}
+{% highlight js %}
+db.explain({
+ selector: {
+ name: 'Mario',
+ series: "mario"
+ },
+ fields: ['_id', 'name'],
+ sort: ['name']
+}).then(function (explanation) {
+ // view explanation
+}).catch(function (err) {
+ console.log(err);
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+#### Example Response:
+
+{% highlight js %}
+{
+ "dbname": "database-name"
+ "index": {
+ "ddoc": "_design/design-doc-name",
+ "name": "index-name",
+ "type": "json",
+ "def": {
+ "fields": [
+ {
+ "name": "asc"
+ },
+ {
+ "series": "asc"
+ }
+ ]
+ }
+ },
+ "selector": {
+ "$and": [
+ {
+ "name": {
+ "$eq": "mario"
+ }
+ },
+ {
+ "series": {
+ "$eq": "mario"
+ }
+ }
+ ]
+ },
+ "opts": {
+ "use_index": [],
+ "bookmark": "nil",
+ "limit": 25,
+ "skip": 0,
+ "sort": {"name": "asc"},
+ "fields": [
+ "_id"
+ ],
+ "r": [
+ 49
+ ],
+ "conflicts": false
+ },
+ "limit": 10,
+ "skip": 1,
+ "fields": [
+ "_id"
+ ],
+ "range": {
+ "start_key": [
+ "mario",
+ "mario"
+ ],
+ "end_key": [
+ "mario",
+ "mario",
+ {}
+ ]
+ }
+};
+{% endhighlight %}
diff --git a/docs/version/latest/api/fetch_document.html b/docs/version/latest/api/fetch_document.html
new file mode 100644
index 0000000000..0897a24f09
--- /dev/null
+++ b/docs/version/latest/api/fetch_document.html
@@ -0,0 +1,64 @@
+
+{% include anchor.html edit="true" title="Fetch a document" hash="fetch_document"%}
+
+{% highlight js %}
+db.get(docId, [options], [callback])
+{% endhighlight %}
+
+Retrieves a document, specified by `docId`.
+
+### Options
+
+All options default to `false` unless otherwise specified.
+
+* `options.rev`: Fetch specific revision of a document. Defaults to winning revision (see [the CouchDB guide](http://guide.couchdb.org/draft/conflicts.html)).
+* `options.revs`: Include revision history of the document.
+* `options.revs_info`: Include a list of revisions of the document, and their availability.
+* `options.open_revs`: Fetch all leaf revisions if `open_revs="all"` or fetch all leaf revisions specified in `open_revs` array. Leaves will be returned in the same order as specified in input array.
+* `options.conflicts`: If specified, conflicting leaf revisions will be attached in `_conflicts` array.
+* `options.attachments`: Include attachment data.
+ * `options.binary`: Return attachment data as Blobs/Buffers, instead of as base64-encoded strings.
+* `options.latest`: Forces retrieving latest “leaf” revision, no matter what rev was requested. Default is `false`.
+
+#### Example Usage:
+
+{% include code/start.html id="get1" type="callback" %}
+{% highlight js %}
+db.get('mydoc', function(err, doc) {
+ if (err) { return console.log(err); }
+ // handle doc
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="get1" type="async" %}
+{% highlight js %}
+try {
+ const doc = await db.get('mydoc');
+} catch (err) {
+ console.log(err);
+}
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="get1" type="promise" %}
+{% highlight js %}
+db.get('mydoc').then(function (doc) {
+ // handle doc
+}).catch(function (err) {
+ console.log(err);
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+#### Example Response:
+{% highlight js %}
+{
+ "_id": "mydoc",
+ "_rev": "1-A6157A5EA545C99B00FF904EEF05FD9F"
+ "title": "Rock and Roll Heart",
+}
+{% endhighlight %}
+
+The response contains the document as it is stored in the database, along with its
+`_id` and `_rev`.
diff --git a/docs/version/latest/api/get_attachment.html b/docs/version/latest/api/get_attachment.html
new file mode 100644
index 0000000000..5578176228
--- /dev/null
+++ b/docs/version/latest/api/get_attachment.html
@@ -0,0 +1,116 @@
+{% include anchor.html edit="true" title="Get an attachment" hash="get_attachment" %}
+
+{% highlight js %}
+db.getAttachment(docId, attachmentId, [options], [callback])
+{% endhighlight %}
+
+Get attachment data.
+
+### Options
+
+* `options.rev`: as with [get()](#fetch_document), you can pass a `rev` in and get back an attachment for the document at that particular revision.
+
+#### Example Usage:
+
+Get an attachment with filename `'att.txt'` from document with ID `'doc'`:
+
+{% include code/start.html id="get_att1" type="callback" %}
+{% highlight js %}
+db.getAttachment('doc', 'att.txt', function(err, blobOrBuffer) {
+ if (err) { return console.log(err); }
+ // handle result
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="get_att1" type="async" %}
+{% highlight js %}
+try {
+ const blobOrBuffer = await db.getAttachment('doc', 'att.txt');
+} catch (err) {
+ console.log(err);
+}
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="get_att1" type="promise" %}
+{% highlight js %}
+db.getAttachment('doc', 'att.txt').then(function (blobOrBuffer) {
+ // handle result
+}).catch(function (err) {
+ console.log(err);
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+Get an attachment with filename `'att.txt'` from document with ID `'doc'`, at
+the revision `'1-abcd'`:
+
+{% include code/start.html id="get_att2" type="callback" %}
+{% highlight js %}
+db.getAttachment('doc', 'att.txt', {rev: '1-abcd'}, function(err, blobOrBuffer) {
+ if (err) { return console.log(err); }
+ // handle result
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="get_att2" type="async" %}
+{% highlight js %}
+try {
+ const blobOrBuffer = await db.getAttachment('doc', 'att.txt', {rev: '1-abcd'});
+} catch (err) {
+ console.log(err);
+}
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="get_att2" type="promise" %}
+{% highlight js %}
+db.getAttachment('doc', 'att.txt', {rev: '1-abcd'}).then(function (blobOrBuffer) {
+ // handle result
+}).catch(function (err) {
+ console.log(err);
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+#### Response type:
+
+The response will be a `Blob` object in the browser, and a `Buffer` object in Node.js. See [blob-util](https://github.com/nolanlawson/blob-util) for utilities to transform `Blob`s to other formats, such as base64-encoded strings, data URLs, array buffers, etc.
+
+#### Inline base64 attachments
+
+You can specify `{attachments: true}` to most "read" operations, such as `get()`, `allDocs()`, `changes()`, and `query()`. The attachment data will then be included inlined in the resulting doc(s). However, it will always be supplied as base64. For example:
+
+{% highlight js %}
+{
+ "_attachments": {
+ "att.txt": {
+ "content_type": "text/plain",
+ "digest": "d5ccfd24a8748bed4e2c9a279a2b6089",
+ "data": "SXMgdGhlcmUgbGlmZSBvbiBNYXJzPw=="
+ }
+ },
+ "_id": "mydoc",
+ "_rev": "1-e147d9ec9c85139dfe7e93bc17148d1a"
+}
+{% endhighlight %}
+
+For such APIs, when you don't specify `{attachments: true}`, you will instead get metadata about the attachments. For example:
+
+{% highlight js %}
+{
+ "_attachments": {
+ "att.txt": {
+ "content_type": "text/plain",
+ "digest": "d5ccfd24a8748bed4e2c9a279a2b6089",
+ "stub": true
+ }
+ },
+ "_id": "mydoc",
+ "_rev": "1-e147d9ec9c85139dfe7e93bc17148d1a"
+}
+{% endhighlight %}
+
+This "summary" operation may be faster in some cases, because the attachment itself does not need to be read from disk.
\ No newline at end of file
diff --git a/docs/version/latest/api/list_indexes.html b/docs/version/latest/api/list_indexes.html
new file mode 100644
index 0000000000..fa24c9be11
--- /dev/null
+++ b/docs/version/latest/api/list_indexes.html
@@ -0,0 +1,84 @@
+{% include anchor.html edit="true" title="List indexes" hash="list_indexes" %}
+
+{% highlight js %}
+db.getIndexes([callback])
+{% endhighlight %}
+
+Get a list of all the indexes you've created. Also tells you about the
+special `_all_docs` index, i.e. the default index on the `_id` field.
+
+{% include alert/start.html variant="info"%}
+{% markdown %}
+
+**pouchdb-find plugin needed:** This API requires the `pouchdb-find` plugin. See
+[Mango queries](/guides/mango-queries.html) for installation instructions.
+
+{% endmarkdown %}
+{% include alert/end.html%}
+
+#### Example Usage:
+
+{% include code/start.html id="get_idxs" type="callback" %}
+{% highlight js %}
+db.getIndexes(function (err, result) {
+ if (err) { return console.log(err); }
+ // handle result
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="get_idxs" type="async" %}
+{% highlight js %}
+try {
+ const result = await db.getIndexes();
+} catch (err) {
+ console.log(err);
+}
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="get_idxs" type="promise" %}
+{% highlight js %}
+db.getIndexes().then(function (result) {
+ // handle result
+}).catch(function (err) {
+ console.log(err);
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+#### Example Response:
+
+{% highlight js %}
+{
+ "indexes": [
+ {
+ "ddoc": null,
+ "name": "_all_docs",
+ "type": "special",
+ "def": {
+ "fields": [
+ {
+ "_id": "asc"
+ }
+ ]
+ }
+ },
+ {
+ "ddoc": "_design/idx-0f3a6f73110868266fa5c688caf8acd3",
+ "name": "idx-0f3a6f73110868266fa5c688caf8acd3",
+ "type": "json",
+ "def": {
+ "fields": [
+ {
+ "foo": "asc"
+ },
+ {
+ "bar": "asc"
+ }
+ ]
+ }
+ }
+ ]
+}
+{% endhighlight %}
\ No newline at end of file
diff --git a/docs/version/latest/api/overview.html b/docs/version/latest/api/overview.html
new file mode 100644
index 0000000000..02df9305fd
--- /dev/null
+++ b/docs/version/latest/api/overview.html
@@ -0,0 +1,59 @@
+{% include anchor.html edit="true" title="API overview" hash="overview" %}
+
+PouchDB has an asynchronous API, supporting [callbacks](http://docs.nodejitsu.com/articles/getting-started/control-flow/what-are-callbacks), [promises][promise], and
+[async functions](https://jakearchibald.com/2014/es7-async-functions/). For beginners, we recommend promises, although you are free to use whatever format you prefer. If you are unsure, check out our [guide to asynchronous code](/guides/async-code.html).
+
+Most of the API is exposed as:
+
+{% highlight js %}
+db.doSomething(args..., [options], [callback])
+{% endhighlight %}
+
+… where both the `options` and `callback` are optional.
+
+### Callbacks
+
+Callbacks use the standard Node.js idiom of:
+
+{% highlight js %}
+function(error, result) { /* ... */ }
+{% endhighlight %}
+
+… where the `error` will be undefined if there's no error.
+
+### Promises
+
+If you don't specify a `callback`, then the API returns a [promise][]. In [supported browsers](http://caniuse.com/#feat=promises) or Node.js, native promises are used, falling back to the minimal library [lie][] as needed.
+
+{% include alert/start.html variant="info"%}
+{% markdown %}
+
+**Using Ionic/AngularJS?** You can wrap PouchDB promises in [`$q.when()`](https://docs.angularjs.org/api/ng/service/$q#when). This will notify AngularJS to update the UI when the PouchDB promise has resolved.
+
+{% endmarkdown %}
+{% include alert/end.html%}
+
+To use a custom promise implementation with PouchDB, you must redefine a global `Promise` object before loading PouchDB:
+
+{% highlight html %}
+
+
+{% endhighlight %}
+
+### Async functions
+
+Another option is to use `async`/`await` pattern instead of promises chain (ie. `.then().catch()`). `async`/`await` is widely supported by all major browsers and Node.js. Use a transpiler with `async`/`await` polyfill such as [Babel](http://babeljs.io/) if you need to support older browsers.
+
+Note that the samples for `async`/`await` in the API documentation assume that your code is inside an async function. So for instance:
+
+{% highlight js %}
+async function myFunction() {
+ // your code goes in here
+}
+{% endhighlight %}
+
+Any `await` not inside of an async function is a syntax error. For more information about `async`/`await`, read [our introductory blog post]({{ site.baseurl }}/2015/03/05/taming-the-async-beast-with-es7.html).
+
+[promise]: https://www.promisejs.org/
+[lie]: https://github.com/calvinmetcalf/lie
+[event emitter]: http://nodejs.org/api/events.html#events_class_events_eventemitter
diff --git a/docs/version/latest/api/plugins.html b/docs/version/latest/api/plugins.html
new file mode 100644
index 0000000000..06b29c1113
--- /dev/null
+++ b/docs/version/latest/api/plugins.html
@@ -0,0 +1,121 @@
+{% include anchor.html edit="true" title="Plugins" hash="plugins" %}
+
+{% include alert/start.html variant="info"%}
+{% markdown %}
+
+**This section covers _authoring_ plugins.** For a list of third-party plugins,
+see [Plugins](/external.html), or for a list of first-party plugins that you can
+use to customize the PouchDB build, see [Custom Builds](/custom.html).
+
+{% endmarkdown %}
+{% include alert/end.html%}
+
+Writing a plugin is easy! The API is:
+
+{% highlight js %}
+PouchDB.plugin({
+ methodName: myFunction
+});
+{% endhighlight %}
+
+This will add a `db.methodName()` to all databases, which runs `myFunction`.It will always be called in context, so that within the function, `this` refers to the database object.
+
+There is a [PouchDB Plugin Seed project](https://github.com/pouchdb/plugin-seed), which is the fastest way to get started writing, building and testing your very own plugin.
+
+#### Example Usage:
+{% highlight js %}
+PouchDB.plugin({
+ sayHello : function () {
+ console.log("Hello!");
+ }
+});
+new PouchDB('foobar').sayHello(); // prints "Hello!"
+{% endhighlight %}
+
+Alternatively, instead of passing in an object to `.plugin()`, you can pass in
+a function that takes the `PouchDB` object and performs whatever operations
+you want on it. You can use this to load multiple plugins, add adapters,
+or attach event listeners to the `PouchDB` object.
+
+#### Example Usage:
+{% highlight js %}
+PouchDB.plugin(function (PouchDB) {
+ PouchDB.hello = 'world';
+});
+console.log(PouchDB.hello); // prints "world"
+{% endhighlight %}
+
+(Most likely, if you are writing a PouchDB plugin, you will export either the
+object-style or the function-style plugin, so that your users can then
+attach it to their PouchDB object.)
+
+#### Load Plugins from require()
+
+You can load plugins into PouchDB when you load it via `require()`.
+
+{% highlight js %}
+const greet = {sayHello: function() { console.log("Hello!"); }};
+
+const PouchDB = require('pouchdb').plugin(greet);
+
+const db = new PouchDB('foobar');
+db.sayHello(); // prints "Hello!"
+{% endhighlight %}
+
+You can chain plugins, as well:
+
+{% highlight js %}
+const greet = {sayHello: function() { console.log("Hello!"); }};
+const manners = {thank: function(name) { console.log("Thank you, " + name); }};
+
+const PouchDB = require('pouchdb')
+ .plugin(greet)
+ .plugin(manners);
+
+const db = new PouchDB('foobar');
+db.sayHello(); // prints "Hello!"
+db.thank('Mom'); // prints "Thank you, Mom"
+{% endhighlight %}
+
+#### Example Plugin: Intercept Updates
+
+A useful feature of plugins is to intercept updates before they are stored in PouchDB. In this way, a plugin might validate that the data is correct for the application, or even alter documents before they are committed to the database.
+
+The best way to intercept all updates to a PouchDB database is to **override the `bulkDocs()` method**. All changes to PouchDB documents ultimately pass through the `bulkDocs()` method. For example, a call to `put()` will become a `bulkDocs()` call with a "batch" of one document.
+
+Because PouchDB guarantees to plugin authors that all data changes ultimately happen via `bulkDocs()`, it is the ideal place for an application or plugin to intercept updates.
+
+{% highlight js %}
+// Keep a reference to the "upstream" function.
+const pouchBulkDocs = PouchDB.prototype.bulkDocs;
+PouchDB.plugin({bulkDocs: validBulkDocs});
+
+function validBulkDocs(body, options, callback) {
+ if (typeof options == 'function') {
+ callback = options
+ options = {}
+ }
+
+ let docs;
+ if (Array.isArray(body)) {
+ docs = body;
+ } else {
+ docs = body.docs;
+ }
+
+ // All documents must have a .name field.
+ for (let i = 0; i < docs.length; i++) {
+ if (!docs[i].name) {
+ const id = doc._id || '(no _id given)';
+ return callback(new Error('Document is missing .name field: ' + id));
+ }
+ }
+
+ // All documents check out. Pass them to PouchDB.
+ return pouchBulkDocs.call(this, docs, options, callback);
+}
+{% endhighlight %}
+
+The above plugin would return an error if anything ever attempts to store an unnamed document, including documents which change during replication.
+
+Note: this is a very, very simple validation example. It does not behave, for example, like the Apache CouchDB `validate_doc_update()` API.
diff --git a/docs/version/latest/api/purge.html b/docs/version/latest/api/purge.html
new file mode 100644
index 0000000000..54374a8026
--- /dev/null
+++ b/docs/version/latest/api/purge.html
@@ -0,0 +1,79 @@
+{% include anchor.html edit="true" title="Purge a document rev" hash="purge" %}
+
+{% highlight js %}
+db.purge(docId, rev)
+{% endhighlight %}
+
+Purges a specific revision of a document, specified by `docId` and `rev`. `rev` must be a leaf revision.
+
+Purge permanently removes data from the database. Normal deletion with `db.remove()` does not, it only marks the document as `_deleted=true` and creates a new revision. This behaviour ensures that deletes can be replicated across databases, and deleted documents don’t get undeleted by syncing with a database that still has this document.
+
+`db.purge()` is not intended as a regular method for deleting documents, instead, it is meant as an admin function for cases where some secret was erroneously added to the database and must now be removed completely, eg. a credit card or social security number. Purge effectively puts the database in a state where the offending write never happened.
+
+{% include alert/start.html variant="info"%}
+{% markdown %}
+
+**Purge is currently only implemented in the `indexeddb` adapter**.
+
+Using Purge with any other adapter will return an error.
+
+{% endmarkdown %}
+{% include alert/end.html%}
+
+#### Example Usage:
+
+{% include code/start.html id="purge1" type="callback" %}
+{% highlight js %}
+db.purge('mydoc', '6-3a24009a9525bde9e4bfa8a99046b00d',
+ function (err, result) {
+ if (err) { return console.log(err); }
+ // handle result
+);
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="purge1" type="async" %}
+{% highlight js %}
+try {
+ const result = await db.purge('mydoc', '6-3a24009a9525bde9e4bfa8a99046b00d');
+ // handle result
+} catch (err) {
+ console.log(err);
+}
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="purge1" type="promise" %}
+{% highlight js %}
+db.purge('mydoc', '6-3a24009a9525bde9e4bfa8a99046b00d')
+ .then(function (result) {
+ // handle result
+ }).catch(function (err) {
+ console.log(err);
+ });
+{% endhighlight %}
+{% include code/end.html %}
+
+#### Example Response:
+
+{% highlight js %}
+ {
+ "ok": true,
+ "deletedRevs": [
+ "6-3a24009a9525bde9e4bfa8a99046b00d",
+ "5-df4a81cd21c75c71974d96e88a68fc2f"
+ ],
+ "documentWasRemovedCompletely": false
+ }
+{% endhighlight %}
+
+If the document has no conflicts, purging its only leaf rev deletes the document completely, and `purge()` returns `documentWasRemovedCompletely: true` in its result object.
+
+If the document does have conflicts, purging will remove the specified rev and pick a leaf from another rev branch as the winner. Fetching the doc after the purge will return the updated state of the document with the new winning revision in `_rev`.
+
+**Notes:**
+
+* The `rev` specified for purging must be a leaf, otherwise an error will be returned
+* Purges do not sync, they only apply to the database they are performed on
+* If the document has conflicts, purging a leaf will also remove its ancestors up to the next branching rev, and pick a new winning rev
+* Any attachments referenced in the specified rev will also be deleted
diff --git a/docs/version/latest/api/query_database.html b/docs/version/latest/api/query_database.html
new file mode 100644
index 0000000000..073ce32fe0
--- /dev/null
+++ b/docs/version/latest/api/query_database.html
@@ -0,0 +1,459 @@
+{% include anchor.html edit="true" title="Map/reduce queries" hash="query_database" %}
+
+{% highlight js %}
+db.query(fun, [options], [callback])
+{% endhighlight %}
+
+Invoke a map/reduce function, which allows you to perform more complex queries on PouchDB than what you get with [allDocs()](#batch_fetch), [changes()](#changes), or [find()](#query_index). The [CouchDB documentation for map/reduce](https://docs.couchdb.org/en/stable/couchapp/views/intro.html) applies to PouchDB.
+
+Since views perform a full scan of all documents, this method may be slow, unless you first save your view in a design document. Read the [query guide](/guides/queries.html) for a good tutorial.
+
+{% include alert/start.html variant="warning"%}
+{% markdown %}
+
+**Warning: advanced API.** The map/reduce API is designed for cases that are too complex for Mango queries, which are described in
+[createIndex()](#create_index), [find()](#query_index), [listIndexes()](#list_indexes), and [deleteIndex()](#delete_index).
+
+Under the hood, Mango indexes are the same as map/reduce indexes. The Mango API is just a simplified user-facing API on top of map/reduce.
+
+{% endmarkdown %}
+{% include alert/end.html%}
+
+### Options
+
+All options default to `false` unless otherwise specified.
+
+* `fun`: Map/reduce function, which can be one of the following:
+ * A full CouchDB-style map/reduce view: `{map : ..., reduce: ...}`.
+ * A map function by itself (no reduce).
+ * The name of a view in an existing design document (e.g. `'mydesigndoc/myview'`, or `'myview'` as a shorthand for `'myview/myview'`).
+* `options.reduce`: Defaults to `true` when a reduce function is defined, or `false` otherwise. Valid values:
+ * `true` - calls the defined `reduce` function, or the `map` function if no `reduce` is defined.
+ * `false` - calls the defined `map` function.
+ * A reduce function.
+ * The string name of a built-in function: `'_sum'`, `'_count'`, or `'_stats'`.
+ * Tip: if you're not using a built-in, [you're probably doing it wrong](http://youtu.be/BKQ9kXKoHS8?t=865s).
+ * PouchDB will always call your reduce function with rereduce == false. As for CouchDB, refer to the [CouchDB documentation](https://docs.couchdb.org/en/stable/couchapp/views/intro.html).
+* `options.include_docs`: Include the document in each row in the `doc` field.
+ - `options.conflicts`: Include conflicts in the `_conflicts` field of a doc.
+ - `options.attachments`: Include attachment data.
+ - `options.binary`: Return attachment data as Blobs/Buffers, instead of as base64-encoded strings.
+* `options.startkey` & `options.endkey`: Get rows with keys in a certain range (inclusive/inclusive).
+* `options.inclusive_end`: Include rows having a key equal to the given `options.endkey`. Default: `true`.
+* `options.limit`: Maximum number of rows to return.
+* `options.skip`: Number of rows to skip before returning (warning: poor performance on IndexedDB/LevelDB!).
+* `options.descending`: Reverse the order of the output rows.
+* `options.key`: Only return rows matching this key.
+* `options.keys`: Array of keys to fetch in a single shot.
+ - Neither `startkey` nor `endkey` can be specified with this option.
+ - The rows are returned in the same order as the supplied `keys` array.
+ - The row for a deleted document will have the revision ID of the deletion, and an extra key `"deleted":true` in the `value` property.
+ - The row for a nonexistent document will just contain an `"error"` property with the value `"not_found"`.
+* `options.group`: True if you want the reduce function to group results by keys, rather than returning a single result. Defaults to `false`.
+* `options.group_level`: Number of elements in a key to group by, assuming the keys are arrays. Defaults to the full length of the array.
+* `options.stale`: One of `'ok'` or `'update_after'`. Only applies to saved views. Can be one of:
+ * unspecified (default): Returns the latest results, waiting for the view to build if necessary.
+ * `'ok'`: Returns results immediately, even if they're out-of-date.
+ * `'update_after'`: Returns results immediately, but kicks off a build afterwards.
+* `options.update_seq`: Include an `update_seq` value indicating which sequence id of the underlying database the view reflects.
+
+For details, see the [CouchDB query options documentation](https://docs.couchdb.org/en/stable/api/ddoc/views.html#get--db-_design-ddoc-_view-view).
+
+#### Example Usage:
+
+{% include code/start.html id="query1" type="callback" %}
+{% highlight js %}
+// create a design doc
+const ddoc = {
+ _id: '_design/index',
+ views: {
+ index: {
+ map: function mapFun(doc) {
+ if (doc.title) {
+ emit(doc.title);
+ }
+ }.toString()
+ }
+ }
+}
+
+// save the design doc
+db.put(ddoc, function (err) {
+ if (err && err.name !== 'conflict') {
+ return console.log(err);
+ }
+ // ignore if doc already exists
+ // find docs where title === 'Lisa Says'
+ db.query('index', {
+ key: 'Lisa Says',
+ include_docs: true
+ }, function (err, result) {
+ if (err) { return console.log(err); }
+ // handle result
+ });
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="query1" type="async" %}
+{% highlight js %}
+// create a design doc
+const ddoc = {
+ _id: '_design/index',
+ views: {
+ index: {
+ map: function mapFun(doc) {
+ if (doc.title) {
+ emit(doc.title);
+ }
+ }.toString()
+ }
+ }
+}
+
+// save the design doc
+try {
+ try {
+ await db.put(ddoc);
+ } catch (err) {
+ if (err.name !== 'conflict') {
+ throw err;
+ }
+ // ignore if doc already exists
+ }
+ // find docs where title === 'Lisa Says'
+ const result = await db.query('index', {
+ key: 'Lisa Says',
+ include_docs: true
+ });
+} catch (err) {
+ console.log(err);
+}
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="query1" type="promise" %}
+{% highlight js %}
+// create a design doc
+const ddoc = {
+ _id: '_design/index',
+ views: {
+ index: {
+ map: function mapFun(doc) {
+ if (doc.title) {
+ emit(doc.title);
+ }
+ }.toString()
+ }
+ }
+}
+
+// save the design doc
+db.put(ddoc).catch(function (err) {
+ if (err.name !== 'conflict') {
+ throw err;
+ }
+ // ignore if doc already exists
+}).then(function () {
+ // find docs where title === 'Lisa Says'
+ return db.query('index', {
+ key: 'Lisa Says',
+ include_docs: true
+ });
+}).then(function (result) {
+ // handle result
+}).catch(function (err) {
+ console.log(err);
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+#### Example Response:
+{% highlight js %}
+{
+ "offset" : 0,
+ "rows": [{
+ "id": "doc3",
+ "key": "Lisa Says",
+ "value": null,
+ "doc": {
+ "_id": "doc3",
+ "_rev": "1-z",
+ "title": "Lisa Says"
+ }
+ }],
+ "total_rows" : 4
+}
+{% endhighlight %}
+
+In the result,`total_rows` is the total number of possible results in the view. The response is very similar to that of `allDocs()`.
+
+The result may also have `update_seq` if you set `update_seq` to `true`
+
+**Note:** you can also pass in the map function instead of saving a design doc first, but this is slow because it has to do a full database scan. The following examples will use this pattern for simplicity's sake, but you should normally avoid it.
+
+#### Complex keys
+
+You can also use [complex keys](https://docs.couchdb.org/en/stable/ddocs/views/collation.html#complex-keys) for fancy ordering:
+
+{% include code/start.html id="query2" type="callback" %}
+{% highlight js %}
+function map(doc) {
+ // sort by last name, first name, and age
+ emit([doc.lastName, doc.firstName, doc.age]);
+}
+db.query(map, function (err, response) {
+ if (err) { return console.log(err); }
+ // handle result
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="query2" type="async" %}
+{% highlight js %}
+function map(doc) {
+ // sort by last name, first name, and age
+ emit([doc.lastName, doc.firstName, doc.age]);
+}
+try {
+ const result = await db.query(map);
+} catch (err) {
+ console.log(err);
+}
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="query2" type="promise" %}
+{% highlight js %}
+function map(doc) {
+ // sort by last name, first name, and age
+ emit([doc.lastName, doc.firstName, doc.age]);
+}
+db.query(map).then(function (result) {
+ // handle result
+}).catch(function (err) {
+ console.log(err);
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+#### Example Response:
+{% highlight js %}
+{
+ "offset": 0,
+ "rows": [{
+ "id" : "bowie",
+ "key" : ["Bowie", "David", 67]
+ }, {
+ "id" : "dylan",
+ "key" : ["Dylan", "Bob", 72]
+ }, {
+ "id" : "younger_dylan",
+ "key" : ["Dylan", "Jakob", 44]
+ }, {
+ "id" : "hank_the_third",
+ "key" : ["Williams", "Hank", 41]
+ }, {
+ "id" : "hank",
+ "key" : ["Williams", "Hank", 91]
+ }],
+ "total_rows": 5
+}
+{% endhighlight %}
+
+**Tips:**
+
+* The sort order is `[nulls, booleans, numbers, strings, arrays, objects]`, so `{startkey: ['Williams'], endkey: ['Williams', {}]}` would return all people with the last name `'Williams'` because objects are higher than strings. Something like `'zzzzz'` or `'\ufff0'` would also work.
+* `group_level` can be very helpful when working with complex keys. In the example above, you can use `{group_level: 1}` to group by last name, or `{group_level: 2}` to group by last and first name. (Be sure to set `{reduce: true, group: true}` as well.)
+
+#### Linked documents
+
+PouchDB fully supports [linked documents](https://docs.couchdb.org/en/stable/ddocs/views/joins.html?highlight=linked%20documents#linked-documents). Use them to join two types of documents together, by simply adding an `_id` to the emitted value:
+
+{% include code/start.html id="query3" type="callback" %}
+{% highlight js %}
+function map(doc) {
+ // join artist data to albums
+ if (doc.type === 'album') {
+ emit(doc.name, {_id : doc.artistId, albumYear : doc.year});
+ }
+}
+db.query(map, {include_docs : true}, function (err, response) {
+ if (err) { return console.log(err); }
+ // handle result
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="query3" type="async" %}
+{% highlight js %}
+function map(doc) {
+ // join artist data to albums
+ if (doc.type === 'album') {
+ emit(doc.name, {_id : doc.artistId, albumYear : doc.year});
+ }
+}
+try {
+ const result = await db.query(map, {include_docs : true});
+} catch (err) {
+ console.log(err);
+}
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="query3" type="promise" %}
+{% highlight js %}
+function map(doc) {
+ // join artist data to albums
+ if (doc.type === 'album') {
+ emit(doc.name, {_id : doc.artistId, albumYear : doc.year});
+ }
+}
+db.query(map, {include_docs : true}).then(function (result) {
+ // handle result
+}).catch(function (err) {
+ console.log(err);
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+#### Example response:
+
+{% highlight js %}
+{
+ "offset": 0,
+ "rows": [
+ {
+ "doc": {
+ "_id": "bowie",
+ "_rev": "1-fdb234b78904a5c8293f2acf4be70d44",
+ "age": 67,
+ "firstName": "David",
+ "lastName": "Bowie"
+ },
+ "id": "album_hunkeydory",
+ "key": "Hunky Dory",
+ "value": {
+ "_id": "bowie",
+ "albumYear": 1971
+ }
+ },
+ {
+ "doc": {
+ "_id": "bowie",
+ "_rev": "1-fdb234b78904a5c8293f2acf4be70d44",
+ "age": 67,
+ "firstName": "David",
+ "lastName": "Bowie"
+ },
+ "id": "album_low",
+ "key": "Low",
+ "value": {
+ "_id": "bowie",
+ "albumYear": 1977
+ }
+ },
+ {
+ "doc": {
+ "_id": "bowie",
+ "_rev": "1-fdb234b78904a5c8293f2acf4be70d44",
+ "age": 67,
+ "firstName": "David",
+ "lastName": "Bowie"
+ },
+ "id": "album_spaceoddity",
+ "key": "Space Oddity",
+ "value": {
+ "_id": "bowie",
+ "albumYear": 1969
+ }
+ }
+ ],
+ "total_rows": 3
+}
+{% endhighlight %}
+
+#### Closures
+
+If you pass a function to `db.query` and give it the `emit` function as the second argument, then you can use a closure. (Since PouchDB has to use `eval()` to bind `emit`.)
+
+{% include code/start.html id="query4" type="callback" %}
+{% highlight js %}
+// BAD! will throw error
+const myId = 'foo';
+db.query(function(doc) {
+ if (doc._id === myId) {
+ emit(doc);
+ }
+}, function(err, results) {
+ // you'll get an error here
+});
+
+// will be fine
+const myId = 'foo';
+db.query(function(doc, emit) {
+ if (doc._id === myId) {
+ emit(doc);
+ }
+}, function(err, results) {
+ if (err) { return console.log(err); }
+ // handle result
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="query4" type="async" %}
+{% highlight js %}
+// BAD! will throw error
+const myId = 'foo';
+try {
+ const result = await db.query(function(doc) {
+ if (doc._id === myId) {
+ emit(doc);
+ }
+ });
+} catch (err) {
+ // you'll get an error here
+}
+
+// will be fine
+const myId = 'foo';
+try {
+ const result = await db.query(function(doc, emit) {
+ if (doc._id === myId) {
+ emit(doc);
+ }
+ });
+} catch (err) {
+ console.log(err);
+}
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="query4" type="promise" %}
+{% highlight js %}
+// BAD! will throw error
+const myId = 'foo';
+db.query(function(doc) {
+ if (doc._id === myId) {
+ emit(doc);
+ }
+}).catch(function (err) {
+ // you'll get an error here
+}
+
+// will be fine
+const myId = 'foo';
+db.query(function(doc, emit) {
+ if (doc._id === myId) {
+ emit(doc);
+ }
+}).then(function (result) {
+ // handle result
+}).catch(function (err) {
+ console.log(err);
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+Note that closures are only supported by local databases with temporary views. So if you are using closures, then you must use the slower method that requires a full database scan.
diff --git a/docs/version/latest/api/query_index.html b/docs/version/latest/api/query_index.html
new file mode 100644
index 0000000000..7ea7ca8b8a
--- /dev/null
+++ b/docs/version/latest/api/query_index.html
@@ -0,0 +1,338 @@
+{% include anchor.html edit="true" title="Query index" hash="query_index" %}
+
+{% highlight js %}
+db.find(request [, callback])
+{% endhighlight %}
+
+Query an index and return the list of documents that match the request.
+
+{% include alert/start.html variant="info"%}
+{% markdown %}
+
+**pouchdb-find plugin needed:** This API requires the `pouchdb-find` plugin. See
+[Mango queries](/guides/mango-queries.html) for installation instructions.
+
+{% endmarkdown %}
+{% include alert/end.html%}
+
+#### Example Usage:
+
+{% include code/start.html id="query_idx" type="callback" %}
+{% highlight js %}
+db.find({
+ selector: {name: 'Mario'},
+ fields: ['_id', 'name'],
+ sort: ['name']
+}, function (err, result) {
+ if (err) { return console.log(err); }
+ // handle result
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="query_idx" type="async" %}
+{% highlight js %}
+try {
+ const result = await db.find({
+ selector: {name: 'Mario'},
+ fields: ['_id', 'name'],
+ sort: ['name']
+ });
+} catch (err) {
+ console.log(err);
+}
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="query_idx" type="promise" %}
+{% highlight js %}
+db.find({
+ selector: {name: 'Mario'},
+ fields: ['_id', 'name'],
+ sort: ['name']
+}).then(function (result) {
+ // handle result
+}).catch(function (err) {
+ console.log(err);
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+#### Example Response:
+
+{% highlight js %}
+{
+ "docs": [
+ {
+ "_id": "mario",
+ "name": "Mario"
+ }
+ ]
+}
+{% endhighlight %}
+
+The above is a simple example. For an in-depth tutorial, please refer to
+[the Mango guide](/guides/mango-queries.html).
+
+### Options
+
+* `selector` Defines a selector to filter the results. Required.
+ * `$lt` Match fields "less than" this one.
+ * `$gt` Match fields "greater than" this one.
+ * `$lte` Match fields "less than or equal to" this one.
+ * `$gte` Match fields "greater than or equal to" this one.
+ * `$eq` Match fields equal to this one.
+ * `$ne` Match fields not equal to this one.
+ * `$exists` True if the field should exist, false otherwise.
+ * `$type` One of: "null", "boolean", "number", "string", "array", or "object".
+ * `$in` The document field must exist in the list provided.
+ * `$and` Matches if all the selectors in the array match.
+ * `$nin` The document field must not exist in the list provided.
+ * `$all` Matches an array value if it contains all the elements of the argument array.
+ * `$size` Special condition to match the length of an array field in a document.
+ * `$or` Matches if any of the selectors in the array match. All selectors must use the same index.
+ * `$nor` Matches if none of the selectors in the array match.
+ * `$not` Matches if the given selector does not match.
+ * `$mod` Matches documents where (field % Divisor == Remainder) is true, and only when the document field is an integer.
+ * `$regex` A regular expression pattern to match against the document field.
+ * `$elemMatch` Matches all documents that contain an array field with at least one element that matches all the specified query criteria.
+* `fields` (Optional) Defines a list of fields that you want to receive. If omitted, you get the full documents.
+* `sort` (Optional) Defines a list of fields defining how you want to sort. Note that sorted fields also have to be selected in the `selector`.
+* `limit` (Optional) Maximum number of documents to return. Default is `25` when connected to a local database.
+* `skip` (Optional) Number of docs to skip before returning.
+* `use_index` (Optional) Set which index to use for the query. It can be "design-doc-name" or "['design-doc-name', 'name']".
+
+If there's no index that matches your `selector`/`sort`, then this method will issue a warning:
+
+{% highlight js %}
+{
+ "docs": [ /* ... */ ],
+ "warning": "No matching index found, create an index to optimize query time."
+}
+{% endhighlight %}
+
+The best index will be chosen automatically.
+
+See the [CouchDB `_find` documentation](https://docs.couchdb.org/en/stable/api/database/find.html) for more details on
+selectors and the Mango query language.
+
+### More examples
+
+Use `$eq` for "equals":
+
+{% include code/start.html id="query_idx2" type="callback" %}
+{% highlight js %}
+db.find({
+ selector: {name: {$eq: 'Mario'}}
+}, function (err, result) {
+ if (err) { return console.log(err); }
+ // handle result
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="query_idx2" type="async" %}
+{% highlight js %}
+try {
+ const result = await db.find({
+ selector: {name: {$eq: 'Mario'}}
+ });
+} catch (err) {
+ console.log(err);
+}
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="query_idx2" type="promise" %}
+{% highlight js %}
+db.find({
+ selector: {name: {$eq: 'Mario'}}
+}).then(function (result) {
+ // handle result
+}).catch(function (err) {
+ console.log(err);
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+This is equivalent to:
+
+{% include code/start.html id="query_idx3" type="callback" %}
+{% highlight js %}
+db.find({
+ selector: {name: 'Mario'}
+}, function (err, result) {
+ if (err) { return console.log(err); }
+ // handle result
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="query_idx3" type="async" %}
+{% highlight js %}
+try {
+ const result = await db.find({
+ selector: {name: 'Mario'}
+ });
+} catch (err) {
+ console.log(err);
+}
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="query_idx3" type="promise" %}
+{% highlight js %}
+db.find({
+ selector: {name: 'Mario'}
+}).then(function (result) {
+ // handle result
+}).catch(function (err) {
+ console.log(err);
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+You can also do selections on multiple fields. For instance, to
+find all docs where `series` is `'Mario'` and `debut` is greater than `1990`:
+
+{% include code/start.html id="query_idx4" type="callback" %}
+{% highlight js %}
+db.find({
+ selector: {
+ series: 'Mario',
+ debut: { $gt: 1990 }
+ }
+}, function (err, result) {
+ if (err) { return console.log(err); }
+ // handle result
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="query_idx4" type="async" %}
+{% highlight js %}
+try {
+ const result = await db.find({
+ selector: {
+ series: 'Mario',
+ debut: { $gt: 1990 }
+ }
+ });
+} catch (err) {
+ console.log(err);
+}
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="query_idx4" type="promise" %}
+{% highlight js %}
+db.find({
+ selector: {
+ series: 'Mario',
+ debut: { $gt: 1990 }
+ }
+}).then(function (result) {
+ // handle result
+}).catch(function (err) {
+ console.log(err);
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+This is equivalent to:
+
+{% include code/start.html id="query_idx5" type="callback" %}
+{% highlight js %}
+db.find({
+ selector: {
+ $and: [
+ { series: 'Mario' },
+ { debut: { $gt: 1990 } }
+ ]
+ }
+}, function (err, result) {
+ if (err) { return console.log(err); }
+ // handle result
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="query_idx5" type="async" %}
+{% highlight js %}
+try {
+ const result = await db.find({
+ selector: {
+ $and: [
+ { series: 'Mario' },
+ { debut: { $gt: 1990 } }
+ ]
+ }
+ });
+} catch (err) {
+ console.log(err);
+}
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="query_idx5" type="promise" %}
+{% highlight js %}
+db.find({
+ selector: {
+ $and: [
+ { series: 'Mario' },
+ { debut: { $gt: 1990 } }
+ ]
+ }
+}).then(function (result) {
+ // handle result
+}).catch(function (err) {
+ console.log(err);
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+You can also sort the returned documents. For instance, to find all docs sorted by `debut` descending:
+
+{% include code/start.html id="query_idx6" type="callback" %}
+{% highlight js %}
+db.find({
+ selector: {
+ debut: {'$gte': null}
+ },
+ sort: [{debut: 'desc'}]
+}, function (err, result) {
+ if (err) { return console.log(err); }
+ // handle result
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="query_idx6" type="async" %}
+{% highlight js %}
+try {
+ const result = await db.find({
+ selector: {
+ debut: {'$gte': null}
+ },
+ sort: [{debut: 'desc'}]
+ });
+} catch (err) {
+ console.log(err);
+}
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="query_idx6" type="promise" %}
+{% highlight js %}
+db.find({
+ selector: {
+ debut: {'$gte': null}
+ },
+ sort: [{debut: 'desc'}]
+}).then(function (result) {
+ // handle result
+}).catch(function (err) {
+ console.log(err);
+});
+{% endhighlight %}
+{% include code/end.html %}
diff --git a/docs/version/latest/api/replication.html b/docs/version/latest/api/replication.html
new file mode 100644
index 0000000000..7ee4284e07
--- /dev/null
+++ b/docs/version/latest/api/replication.html
@@ -0,0 +1,322 @@
+{% include anchor.html edit="true" title="Replicate a database" hash="replication" %}
+
+{% highlight js %}
+PouchDB.replicate(source, target, [options])
+{% endhighlight %}
+
+Replicate data from `source` to `target`. Both the `source` and `target` can be a PouchDB instance or a string representing a CouchDB database URL or the name of a local PouchDB database. If `options.live` is `true`, then this will track future changes and also replicate them automatically. This method returns an object with the method `cancel()`, which you call if you want to cancel live replication.
+
+Replication is an [event emitter][] like [changes()](#changes) and emits the `'complete'`, `'active'`, `'paused'`, `'change'`, `'denied'`, `'error'` and `'checkpoint'` events.
+
+### Options
+
+All options default to `false` unless otherwise specified.
+
+* `options.live`: If `true`, starts subscribing to future changes in the `source` database and continue replicating them.
+* `options.retry`: If `true` will attempt to retry replications in the case of failure (due to being offline), using a backoff algorithm that retries at longer and longer intervals until a connection is re-established, with a maximum delay of 10 minutes. Only applicable if `options.live` is also `true`.
+
+**Filtering Options:**
+
+* `options.filter`: Reference a filter function from a design document to selectively get updates. To use a view function, pass `_view` here and provide a reference to the view function in `options.view`. See [filtered replication](#filtered-replication) for details.
+* `options.doc_ids`: Only show changes for docs with these ids (array of strings).
+* `options.query_params`: Object containing properties that are passed to the filter function, e.g. `{"foo":"bar"}`, where `"bar"` will be available in the filter function as `params.query.foo`. To access the `params`, define your filter function like `function (doc, params) {/* ... */}`.
+* `options.view`: Specify a view function (e.g. `'design_doc_name/view_name'` or `'view_name'` as shorthand for `'view_name/view_name'`) to act as a filter. Documents counted as "passed" for a view filter if a map function emits at least one record for them. **Note**: `options.filter` must be set to `'_view'` for this option to work.
+* `options.selector`: Filter using a query/pouchdb-find [selector](https://docs.couchdb.org/en/stable/api/database/find.html#find-selectors).
+
+**Advanced Options:**
+
+* `options.since`: Replicate changes after the given sequence number.
+* `options.heartbeat`: Configure the heartbeat supported by CouchDB which keeps the change connection alive.
+* `options.timeout`: Request timeout (in milliseconds).
+* `options.batch_size`: Number of change feed items to process at a time. Defaults to 100. This affects the number of docs and attachments held in memory and the number sent at a time to the target server. You may need to adjust downward if targeting devices with low amounts of memory (e.g. phones) or if the documents and/or attachments are large in size or if there are many conflicted revisions. If your documents are small in size, then increasing this number will probably speed replication up.
+* `options.batches_limit`: Number of batches to process at a time. Defaults to 10. This (along with `batch_size`) controls how many docs are kept in memory at a time, so the maximum docs in memory at once would equal `batch_size` × `batches_limit`.
+* `options.back_off_function`: backoff function to be used in `retry` replication. This is a function that takes the current backoff as input (or 0 the first time) and returns a new backoff in milliseconds. You can use this to tweak when and how replication will try to reconnect to a remote database when the user goes offline. Defaults to a function that chooses a random backoff between 0 and 2 seconds and doubles every time it fails to connect. The default delay will never exceed 10 minutes. (See [Customizing retry replication](#customizing-retry-replication) below.)
+* `options.checkpoint`: Can be used if you want to disable checkpoints on the source, target, or both. Setting this option to `false` will prevent writing checkpoints on both source and target. Setting it to `source` will only write checkpoints on the source. Setting it to `target` will only write checkpoints on the target.
+* `options.style`: Specifies whether all revisions of a document including conflicts and deleted former conflicts (`all_docs`) or only the winning revision (`main_only`) should be replicated. This option is passed to the `changes` endpoint of the replication source. Defaults to `all_docs`.
+
+#### Example Usage:
+
+{% highlight js %}
+const rep = PouchDB.replicate('mydb', 'http://localhost:5984/mydb', {
+ live: true,
+ retry: true
+}).on('change', function (info) {
+ // handle change
+}).on('paused', function (err) {
+ // replication paused (e.g. replication up to date, user went offline)
+}).on('active', function () {
+ // replicate resumed (e.g. new changes replicating, user went back online)
+}).on('denied', function (err) {
+ // a document failed to replicate (e.g. due to permissions)
+}).on('complete', function (info) {
+ // handle complete
+}).on('error', function (err) {
+ // handle error
+});
+
+rep.cancel(); // whenever you want to cancel
+{% endhighlight %}
+
+There are also shorthands for replication given existing PouchDB objects. These behave the same as `PouchDB.replicate()`:
+
+{% highlight js %}
+db.replicate.to(remoteDB, [options]);
+// or
+db.replicate.from(remoteDB, [options]);
+{% endhighlight %}
+
+The `remoteDB` can either be a string or a `PouchDB` object. If you have a fetch override on a remote database, you will want to use `PouchDB` objects instead of strings, so that the options are used.
+
+#### Replication events
+
+* __`change`__ (`info`) - This event fires when the replication has written a new document. `info` will contain details about the change. `info.docs` will contain the docs involved in that change. See below for an example response.
+* __`complete`__ (`info`) - This event fires when replication is completed or cancelled. In a live replication, only cancelling the replication should trigger this event. `info` will contain details about the replication. See below for an example response.
+* __`paused`__ (`err`) - This event fires when the replication is paused, either because a live replication is waiting for changes, or replication has temporarily failed, with `err`, and is attempting to resume.
+* __`active`__ - This event fires when the replication starts actively processing changes; e.g. when it recovers from an error or new changes are available.
+* __`denied`__ (`err`) - This event fires if a document failed to replicate due to validation or authorization errors.
+* __`error`__ (`err`) - This event is fired when the replication is stopped due to an unrecoverable failure. If `retry` is `false`, this will also fire when the user goes offline or another network error occurs (so you can handle retries yourself, if you want).
+* __`checkpoint`__ - This event exposes information about the internal steps of the replication process. It includes one of the fields `pending_batch`, `start_next_batch`, `revs_diff` or `checkpoint`.
+
+#### Single-shot
+
+As with [changes()](#changes), you can also omit `live`, in which case you can use `replicate()` in the callback/promise style and it will be treated as a single-shot operation.
+
+{% include code/start.html id="replication1" type="callback" %}
+{% highlight js %}
+db.replicate.to(remote, function (err, result) {
+ if (err) { return console.log(err); }
+ // handle 'completed' result
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="replication1" type="async" %}
+{% highlight js %}
+try {
+ const result = await db.replicate.to(remote);
+} catch (err) {
+ console.log(err);
+}
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="replication1" type="promise" %}
+{% highlight js %}
+db.replicate.to(remote).then(function (result) {
+ // handle 'completed' result
+}).catch(function (err) {
+ console.log(err);
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+For non-live replications, the returned object is also an event emitter as well as a promise, and you can use all the events described above (except for `'paused'` and `'active'`, which only apply to `retry` replications).
+
+#### Example Response:
+
+Example response in the `'change'` listener:
+
+{% highlight js %}
+{
+ "doc_write_failures": 0,
+ "docs_read": 1,
+ "docs_written": 1,
+ "errors": [],
+ "last_seq": 1,
+ "ok": true,
+ "start_time": "Fri May 16 2014 18:23:12 GMT-0700 (PDT)",
+ "docs": [
+ { _id: 'docId',
+ _rev: '1-e798a1a7eb4247b399dbfec84ca699d4',
+ and: 'data' }
+ ]
+}
+{% endhighlight %}
+
+Example response in the `'complete'` listener:
+
+{% highlight js %}
+{
+ "doc_write_failures": 0,
+ "docs_read": 2,
+ "docs_written": 2,
+ "end_time": "Fri May 16 2014 18:26:00 GMT-0700 (PDT)",
+ "errors": [],
+ "last_seq": 2,
+ "ok": true,
+ "start_time": "Fri May 16 2014 18:26:00 GMT-0700 (PDT)",
+ "status": "complete"
+}
+{% endhighlight %}
+
+Note that replication is supported for both local and remote databases. So you can replicate from local to local or from remote to remote.
+
+However, if you replicate from remote to remote, then the changes will flow through PouchDB. If you want to trigger a server-initiated replication, please use regular ajax to POST to the CouchDB `_replicate` endpoint, as described [in the CouchDB docs](https://docs.couchdb.org/en/stable/api/server/common.html#api-server-replicate).
+
+#### Filtered replication
+
+As with [changes()](#changes), you can filter from the source database using:
+
+* an ad-hoc `filter` function
+* an array of `doc_ids`
+* a `filter` function inside of a design document
+* a `filter` function inside of a design document, with `query_params`
+* a `view` function inside of a design document
+
+If you are replicating from a remote CouchDB, then the first method will run client-side, whereas the last four will filter on the server side. Therefore the last four should be preferred, especially if the database is large, because you want to send as few documents over the wire as possible.
+
+You should also beware trying to use filtered replication to enforce security, e.g. to partition a database per user. A better strategy is the ["one database per user" method](https://github.com/nolanlawson/pouchdb-authentication#couchdb-authentication-recipes).
+
+{% include alert/start.html variant="warning" %}
+
+{% markdown %}
+
+**Deleting filtered docs**: When you use filtered replication, you should avoid using `remove()` to delete documents, because that removes all their fields as well, which means they might not pass the filter function anymore, causing the deleted revision to not be replicated. Instead, set the `doc._deleted` flag to `true` and then use `put()` or `bulkDocs()`.
+
+{% endmarkdown %}
+
+{% include alert/end.html %}
+
+#### Filtering examples
+
+In these examples, we'll work with some mammals. Let's imagine our docs are:
+
+{% highlight js %}
+[
+ {_id: 'a', name: 'Kangaroo', type: 'marsupial'},
+ {_id: 'b', name: 'Koala', type: 'marsupial'},
+ {_id: 'c', name: 'Platypus', type: 'monotreme'}
+]
+{% endhighlight %}
+
+Here are 5 examples using the 5 different systems.
+
+**Example 1: Ad-hoc `filter` function**
+
+*Warning*: this runs client-side, if you are replicating from a remote database.
+
+Filter by `type === 'marsupial'`:
+
+{% highlight js %}
+remote.replicate.to(local, {
+ filter: function (doc) {
+ return doc.type === 'marsupial';
+ }
+});
+{% endhighlight %}
+
+**Example 2: Array of `doc_ids`**
+
+Filter documents with `_id`s `['a', 'c']`.
+
+{% highlight js %}
+remote.replicate.to(local, {
+ doc_ids: ['a', 'c']
+});
+{% endhighlight %}
+
+**Example 3: `filter` function inside of a design document**
+
+First `put()` a design document in the remote database:
+
+{% highlight js %}
+{
+ _id: '_design/mydesign',
+ filters: {
+ myfilter: function (doc) {
+ return doc.type === 'marsupial';
+ }.toString()
+ }
+}
+{% endhighlight %}
+
+Then filter by `type === 'marsupial'`:
+
+{% highlight js %}
+remote.replicate.to(local, {
+ filter: 'mydesign/myfilter'
+});
+{% endhighlight %}
+
+**Example 4: `filter` function inside of a design document, with `query_params`**
+
+This is the most powerful way to filter, because it allows you to pass in arbitrary options to your filter function.
+
+First `put()` a design document in the remote database:
+
+{% highlight js %}
+{
+ _id: '_design/mydesign',
+ filters: {
+ myfilter: function (doc, req) {
+ return doc.type === req.query.type;
+ }.toString()
+ }
+}
+{% endhighlight %}
+
+Then filter by `type === 'marsupial'`:
+
+{% highlight js %}
+remote.replicate.to(local, {
+ filter: 'mydesign/myfilter',
+ query_params: {type: 'marsupial'}
+});
+{% endhighlight %}
+
+**Example 5: `view` function inside of a design document**
+
+This doesn't really offer any advantages compared to the previous two methods, unless you are already using a `view` for map/reduce queries, and you want to reuse it.
+
+Any documents that `emit()` anything will be considered to have passed this filter method.
+
+First `put()` a design document in the remote database:
+
+{% highlight js %}
+{
+ _id: '_design/mydesign',
+ views: {
+ myview: {
+ map: function(doc) {
+ if (doc.type === 'marsupial') {
+ emit(doc._id);
+ }
+ }.toString()
+ }
+ }
+}
+{% endhighlight %}
+
+Then filter by `type === 'marsupial'`:
+
+{% highlight js %}
+remote.replicate.to(local, {
+ filter: '_view',
+ view: 'mydesign/myview'
+});
+{% endhighlight %}
+
+#### Customizing retry replication
+
+During `retry` replication, you can customize the backoff function that determines how long to wait before reconnecting when the user goes offline.
+
+Here's a simple backoff function that starts at 1000 milliseconds and triples it every time a remote request fails:
+
+{% highlight js %}
+
+db.replicate.to(remote, {
+ live: true,
+ retry: true,
+ back_off_function: function (delay) {
+ if (delay === 0) {
+ return 1000;
+ }
+ return delay * 3;
+ }
+});
+
+{% endhighlight %}
+
+The first time a request fails, this function will receive 0 as input. The next time it fails, 1000 will be passed in, then 3000, then 9000, etc. When the user comes back online, the `delay` goes back to 0.
+
+By default, PouchDB uses a backoff function that chooses a random starting number between 0 and 2000 milliseconds and will roughly double every time, with some randomness to prevent client requests from occurring simultaneously.
diff --git a/docs/version/latest/api/revisions_diff.html b/docs/version/latest/api/revisions_diff.html
new file mode 100644
index 0000000000..9bd6c45607
--- /dev/null
+++ b/docs/version/latest/api/revisions_diff.html
@@ -0,0 +1,65 @@
+{% include anchor.html edit="true" title="Document revisions diff" hash="revisions_diff" %}
+
+{% highlight js %}
+db.revsDiff(diff, [callback])
+{% endhighlight %}
+
+Given a set of document/revision IDs, returns the subset of those that do not correspond
+to revisions stored in the database. Primarily used in replication.
+
+#### Example Usage:
+
+{% include code/start.html id="revsdiff1" type="callback" %}
+{% highlight js %}
+db.revsDiff({
+ myDoc1: [
+ "1-b2e54331db828310f3c772d6e042ac9c",
+ "2-3a24009a9525bde9e4bfa8a99046b00d"
+ ]
+}, function (err, result) {
+ if (err) { return console.log(err); }
+ // handle result
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="revsdiff1" type="async" %}
+{% highlight js %}
+try {
+ const result = await db.revsDiff({
+ myDoc1: [
+ "1-b2e54331db828310f3c772d6e042ac9c",
+ "2-3a24009a9525bde9e4bfa8a99046b00d"
+ ]
+ });
+} catch (err) {
+ console.log(err);
+}
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="revsdiff1" type="promise" %}
+{% highlight js %}
+db.revsDiff({
+ myDoc1: [
+ "1-b2e54331db828310f3c772d6e042ac9c",
+ "2-3a24009a9525bde9e4bfa8a99046b00d"
+ ]
+}).then(function (result) {
+ // handle result
+}).catch(function (err) {
+ console.log(err);
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+#### Example Response:
+{% highlight js %}
+{
+ "myDoc1": {
+ "missing": ["2-3a24009a9525bde9e4bfa8a99046b00d"]
+ }
+}
+{% endhighlight %}
+
+
diff --git a/docs/version/latest/api/save_attachment.html b/docs/version/latest/api/save_attachment.html
new file mode 100644
index 0000000000..3f2ac38207
--- /dev/null
+++ b/docs/version/latest/api/save_attachment.html
@@ -0,0 +1,349 @@
+{% include anchor.html edit="true" title="Save an attachment" hash="save_attachment" %}
+
+{% highlight js %}
+db.putAttachment(docId, attachmentId, [rev], attachment, type, [callback]);
+{% endhighlight %}
+
+Attaches a binary object to a document.
+
+This method will update an existing document to add the attachment, so it requires a `rev` if the document already exists. If the document doesn't already exist, then this method will create an empty document containing the attachment.
+
+What's the point of attachments? If you're dealing with large binary data (such as PNGs), you may incur a performance or storage penalty if you naïvely include them as base64- or hex-encoded strings inside your documents. But if you insert the binary data as an attachment, then PouchDB will attempt to store it in [the most efficient way possible]({{ site.baseurl }}/faq.html#data_types).
+
+For details, see the [CouchDB documentation on attachments](https://docs.couchdb.org/en/stable/api/document/attachments.html#put--db-docid-attname).
+
+#### Example Usage:
+
+{% include code/start.html id="attach1" type="callback" %}
+{% highlight js %}
+const attachment = new Blob(['Is there life on Mars?'], {type: 'text/plain'});
+db.putAttachment('doc', 'att.txt', attachment, 'text/plain', function(err, res) {
+ if (err) { return console.log(err); }
+ // handle result
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="attach1" type="async" %}
+{% highlight js %}
+try {
+ const attachment = new Blob(['Is there life on Mars?'], {type: 'text/plain'});
+ const result = await db.putAttachment('doc', 'att.txt', attachment, 'text/plain');
+} catch (err) {
+ console.log(err);
+}
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="attach1" type="promise" %}
+{% highlight js %}
+const attachment = new Blob(['Is there life on Mars?'], {type: 'text/plain'});
+db.putAttachment('doc', 'att.txt', attachment, 'text/plain').then(function (result) {
+ // handle result
+}).catch(function (err) {
+ console.log(err);
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+
+#### Example Response:
+{% highlight js %}
+{
+ "ok": true,
+ "id": "doc",
+ "rev": "2-068E73F5B44FEC987B51354DFC772891"
+}
+{% endhighlight %}
+
+Within Node, you must use a `Buffer` instead of a `Blob`:
+
+{% highlight js %}
+const attachment = new Buffer('Is there life on Mars?');
+{% endhighlight %}
+
+For details, see the [Mozilla docs on `Blob`](https://developer.mozilla.org/en-US/docs/Web/API/Blob) or the [Node docs on `Buffer`](http://nodejs.org/api/buffer.html).
+
+If you need a shim for older browsers that don't support the `Blob` constructor, or you want some convenience methods for Blobs, you can use [blob-util](https://github.com/nolanlawson/blob-util).
+
+#### Save a base64 attachment
+
+If you supply a string instead of a `Blob`/`Buffer`, then it will be assumed to be a base64-encoded string, and will be processed accordingly:
+
+{% include code/start.html id="attach3" type="callback" %}
+{% highlight js %}
+const attachment =
+ "TGVnZW5kYXJ5IGhlYXJ0cywgdGVhciB1cyBhbGwgYXBhcnQKTWFrZS" +
+ "BvdXIgZW1vdGlvbnMgYmxlZWQsIGNyeWluZyBvdXQgaW4gbmVlZA==";
+db.putAttachment('doc', 'att.txt', attachment, 'text/plain', function(err, res) {
+ if (err) { return console.log(err); }
+ // handle result
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="attach3" type="async" %}
+{% highlight js %}
+try {
+ const attachment =
+ "TGVnZW5kYXJ5IGhlYXJ0cywgdGVhciB1cyBhbGwgYXBhcnQKTWFrZS" +
+ "BvdXIgZW1vdGlvbnMgYmxlZWQsIGNyeWluZyBvdXQgaW4gbmVlZA==";
+ const result = await db.putAttachment('doc', 'att.txt', attachment, 'text/plain');
+} catch (err) {
+ console.log(err);
+}
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="attach3" type="promise" %}
+{% highlight js %}
+const attachment =
+ "TGVnZW5kYXJ5IGhlYXJ0cywgdGVhciB1cyBhbGwgYXBhcnQKTWFrZS" +
+ "BvdXIgZW1vdGlvbnMgYmxlZWQsIGNyeWluZyBvdXQgaW4gbmVlZA==";
+db.putAttachment('doc', 'att.txt', attachment, 'text/plain').then(function (result) {
+ // handle result
+}).catch(function (err) {
+ console.log(err);
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+
+#### Save an inline attachment
+
+You can also inline attachments inside the document. The attachment data may be supplied as a base64-encoded string with the `content_type`:
+
+{% include code/start.html id="attach2" type="callback" %}
+{% highlight js %}
+const doc = {
+ "_id": "doc",
+ "title": "Legendary Hearts",
+ "_attachments": {
+ "att.txt": {
+ "content_type": "text/plain",
+ "data": "TGVnZW5kYXJ5IGhlYXJ0cywgdGVhciB1cyBhbGwgYXBhcnQKTWFrZS" +
+ "BvdXIgZW1vdGlvbnMgYmxlZWQsIGNyeWluZyBvdXQgaW4gbmVlZA=="
+ }
+ }
+};
+db.put(doc, function (err, result) {
+ if (err) { return console.log(err); }
+ // handle result
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="attach2" type="async" %}
+{% highlight js %}
+try {
+ const doc = {
+ "_id": "doc",
+ "title": "Legendary Hearts",
+ "_attachments": {
+ "att.txt": {
+ "content_type": "text/plain",
+ "data": "TGVnZW5kYXJ5IGhlYXJ0cywgdGVhciB1cyBhbGwgYXBhcnQKTWFrZS" +
+ "BvdXIgZW1vdGlvbnMgYmxlZWQsIGNyeWluZyBvdXQgaW4gbmVlZA=="
+ }
+ }
+ };
+ const result = await db.put(doc);
+} catch (err) {
+ console.log(err);
+}
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="attach2" type="promise" %}
+{% highlight js %}
+const doc = {
+ "_id": "doc",
+ "title": "Legendary Hearts",
+ "_attachments": {
+ "att.txt": {
+ "content_type": "text/plain",
+ "data": "TGVnZW5kYXJ5IGhlYXJ0cywgdGVhciB1cyBhbGwgYXBhcnQKTWFrZS" +
+ "BvdXIgZW1vdGlvbnMgYmxlZWQsIGNyeWluZyBvdXQgaW4gbmVlZA=="
+ }
+ }
+};
+db.put(doc).then(function (result) {
+ // handle result
+}).catch(function (err) {
+ console.log(err);
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+#### Save an inline Blob/Buffer attachment
+
+You can also inline `Blob`s/`Buffer`s:
+
+{% include code/start.html id="attach4" type="callback" %}
+{% highlight js %}
+const doc = {
+ "_id": "doc",
+ "title": "Legendary Hearts",
+ "_attachments": {
+ "att.txt": {
+ "content_type": "text/plain",
+ "data": new Blob(['Is there life on Mars?'], {type: 'text/plain'})
+ }
+ }
+};
+db.put(doc, function (err, result) {
+ if (err) { return console.log(err); }
+ // handle result
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="attach4" type="async" %}
+{% highlight js %}
+try {
+ const doc = {
+ "_id": "doc",
+ "title": "Legendary Hearts",
+ "_attachments": {
+ "att.txt": {
+ "content_type": "text/plain",
+ "data": new Blob(['Is there life on Mars?'], {type: 'text/plain'})
+ }
+ }
+ };
+ const result = await db.put(doc);
+} catch (err) {
+ console.log(err);
+}
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="attach4" type="promise" %}
+{% highlight js %}
+const doc = {
+ "_id": "doc",
+ "title": "Legendary Hearts",
+ "_attachments": {
+ "att.txt": {
+ "content_type": "text/plain",
+ "data": new Blob(['Is there life on Mars?'], {type: 'text/plain'})
+ }
+ }
+};
+db.put(doc).then(function (result) {
+ // handle result
+}).catch(function (err) {
+ console.log(err);
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+#### Save many attachments at once
+
+The inline approach allows you to save multiple attachments to the same document in a single shot:
+
+{% include code/start.html id="attach5" type="callback" %}
+{% highlight js %}
+const doc = {
+ "_id": "doc",
+ "title": "Legendary Hearts",
+ "_attachments": {
+ "att.txt": {
+ "content_type": "text/plain",
+ "data": new Blob(
+ ["And she's hooked to the silver screen"],
+ {type: 'text/plain'})
+ },
+ "att2.txt": {
+ "content_type": "text/plain",
+ "data": new Blob(
+ ["But the film is a saddening bore"],
+ {type: 'text/plain'})
+ },
+ "att3.txt": {
+ "content_type": "text/plain",
+ "data": new Blob(
+ ["For she's lived it ten times or more"],
+ {type: 'text/plain'})
+ }
+ }
+};
+db.put(doc, function (err, result) {
+ if (err) { return console.log(err); }
+ // handle result
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="attach5" type="async" %}
+{% highlight js %}
+try {
+ const doc = {
+ "_id": "doc",
+ "title": "Legendary Hearts",
+ "_attachments": {
+ "att.txt": {
+ "content_type": "text/plain",
+ "data": new Blob(
+ ["And she's hooked to the silver screen"],
+ {type: 'text/plain'})
+ },
+ "att2.txt": {
+ "content_type": "text/plain",
+ "data": new Blob(
+ ["But the film is a saddening bore"],
+ {type: 'text/plain'})
+ },
+ "att3.txt": {
+ "content_type": "text/plain",
+ "data": new Blob(
+ ["For she's lived it ten times or more"],
+ {type: 'text/plain'})
+ }
+ }
+ };
+ const result = await db.put(doc);
+} catch (err) {
+ console.log(err);
+}
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="attach5" type="promise" %}
+{% highlight js %}
+const doc = {
+ "_id": "doc",
+ "title": "Legendary Hearts",
+ "_attachments": {
+ "att.txt": {
+ "content_type": "text/plain",
+ "data": new Blob(
+ ["And she's hooked to the silver screen"],
+ {type: 'text/plain'})
+ },
+ "att2.txt": {
+ "content_type": "text/plain",
+ "data": new Blob(
+ ["But the film is a saddening bore"],
+ {type: 'text/plain'})
+ },
+ "att3.txt": {
+ "content_type": "text/plain",
+ "data": new Blob(
+ ["For she's lived it ten times or more"],
+ {type: 'text/plain'})
+ }
+ }
+};
+db.put(doc).then(function (result) {
+ // handle result
+}).catch(function (err) {
+ console.log(err);
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+See [Inline Attachments](https://docs.couchdb.org/en/stable/api/document/common.html?highlight=inline%20attachment#creating-multiple-attachments)
+on the CouchDB wiki for details.
+
+
diff --git a/docs/version/latest/api/sync.html b/docs/version/latest/api/sync.html
new file mode 100644
index 0000000000..eb75ed15b7
--- /dev/null
+++ b/docs/version/latest/api/sync.html
@@ -0,0 +1,96 @@
+{% include anchor.html edit="true" title="Sync a database" hash="sync" %}
+
+{% highlight js %}
+const sync = PouchDB.sync(src, target, [options])
+{% endhighlight %}
+
+Sync data from `src` to `target` and `target` to `src`. This is a convenience method for bidirectional data replication.
+
+In other words, this code:
+
+{% highlight js %}
+PouchDB.replicate('mydb', 'http://localhost:5984/mydb');
+PouchDB.replicate('http://localhost:5984/mydb', 'mydb');
+{% endhighlight %}
+
+
+is equivalent to this code:
+
+{% highlight js %}
+PouchDB.sync('mydb', 'http://localhost:5984/mydb');
+{% endhighlight %}
+
+
+### Options
+
+* `options.push` + `options.pull`: Allows you to specify separate [replication options](api.html#replication) for the individual replications.
+
+Replication options such as `filter` passed to sync directly will be passed to both replications. Please refer to [replicate()](api.html#replication) for documentation on those options.
+
+#### Example Usage:
+{% highlight js %}
+const sync = PouchDB.sync('mydb', 'http://localhost:5984/mydb', {
+ live: true,
+ retry: true
+}).on('change', function (info) {
+ // handle change
+}).on('paused', function (err) {
+ // replication paused (e.g. replication up to date, user went offline)
+}).on('active', function () {
+ // replicate resumed (e.g. new changes replicating, user went back online)
+}).on('denied', function (err) {
+ // a document failed to replicate (e.g. due to permissions)
+}).on('complete', function (info) {
+ // handle complete
+}).on('error', function (err) {
+ // handle error
+});
+
+sync.cancel(); // whenever you want to cancel
+{% endhighlight %}
+
+There is also a shorthand for syncing given existing PouchDB objects. This behaves the same as `PouchDB.sync()`:
+
+{% highlight js %}
+db.sync(remoteDB, [options]);
+{% endhighlight %}
+
+It is also possible to combine "one-way" replication and sync for performance reasons.
+When your PouchDB application starts up it could perform a one-off, one-way replication to completion and then initiate the two-way, continuous retryable sync:
+
+{% highlight js %}
+const url = 'http://localhost:5984/mydb';
+const opts = { live: true, retry: true };
+
+// do one way, one-off sync from the server until completion
+db.replicate.from(url).on('complete', function(info) {
+ // then two-way, continuous, retriable sync
+ db.sync(url, opts)
+ .on('change', onSyncChange)
+ .on('paused', onSyncPaused)
+ .on('error', onSyncError);
+}).on('error', onSyncError);
+{% endhighlight %}
+
+The above technique results in fewer HTTP requests being used and better performance than just using `db.sync` on its own.
+
+#### Example Response:
+
+Change events in `sync` have an extra property `direction` which refers to the direction the change was going. Its value will either be `push` or `pull`.
+
+{% highlight js %}
+{ direction: 'push',
+ change:
+ { ok: true,
+ start_time: '2015-10-21T15:26:51.151Z',
+ docs_read: 1,
+ docs_written: 1,
+ doc_write_failures: 0,
+ errors: [],
+ last_seq: 1,
+ docs: [ [Object] ] } }
+{% endhighlight %}
+
+For any further details, please refer to [replicate()](api.html#replication).
+
+
diff --git a/docs/version/latest/api/view_cleanup.html b/docs/version/latest/api/view_cleanup.html
new file mode 100644
index 0000000000..4df285312d
--- /dev/null
+++ b/docs/version/latest/api/view_cleanup.html
@@ -0,0 +1,47 @@
+{% include anchor.html edit="true" title="View cleanup" hash="view_cleanup" %}
+
+{% highlight js %}
+db.viewCleanup([callback])
+{% endhighlight %}
+
+Cleans up any stale map/reduce indexes.
+
+As design docs are deleted or modified, their associated index files (in CouchDB) or companion databases (in local PouchDBs) continue to take up space on disk. `viewCleanup()` removes these unnecessary index files.
+
+See [the CouchDB documentation on view cleanup](http://couchdb.readthedocs.org/en/latest/maintenance/compaction.html#views-cleanup) for details.
+
+#### Example Usage:
+
+{% include code/start.html id="viewcleanup" type="callback" %}
+{% highlight js %}
+db.viewCleanup(function (err, result) {
+ if (err) { return console.log(err); }
+ // handle result
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="viewcleanup" type="async" %}
+{% highlight js %}
+try {
+ const result = await db.viewCleanup();
+} catch (err) {
+ console.log(err);
+}
+{% endhighlight %}
+{% include code/end.html %}
+
+{% include code/start.html id="viewcleanup" type="promise" %}
+{% highlight js %}
+db.viewCleanup().then(function (result) {
+ // handle result
+}).catch(function (err) {
+ console.log(err);
+});
+{% endhighlight %}
+{% include code/end.html %}
+
+#### Example Response:
+{% highlight js %}
+{ "ok" : "true" }
+{% endhighlight %}
\ No newline at end of file
diff --git a/docs/version/latest/custom.md b/docs/version/latest/custom.md
new file mode 100644
index 0000000000..b8095cc4aa
--- /dev/null
+++ b/docs/version/latest/custom.md
@@ -0,0 +1,539 @@
+---
+layout: 2ColLeft.html
+title: Custom Builds
+sidebar: ./nav.html
+---
+
+PouchDB supports custom builds, meaning you can pick and choose the features of
+PouchDB that you want to use, potentially resulting in smaller bundle sizes
+and faster build times.
+
+PouchDB exposes its custom builds via separate packages available on npm. All of
+these packages follow the format `pouchdb-` and can be installed using `npm install`.
+
+Some packages are included by default in the main `pouchdb` package, whereas
+others (including third-party packages) must be installed separately.
+
+{% include alert/start.html variant="warning"%}
+{% markdown %}
+
+Custom builds require an [npm][]-based build system, using a bundler
+like [Browserify][], [Webpack][], [SystemJS][], [Rollup][], or [JSPM][]. Tools like
+[Bower][], as well as direct download of prebuilt JavaScript files, are not supported.
+
+[Browserify]: http://browserify.org/
+[Webpack]: http://webpack.github.io/
+[SystemJS]: https://github.com/systemjs/systemjs
+[JSPM]: http://jspm.io/
+[Rollup]: http://rollupjs.org/
+[npm]: http://npmjs.com/
+[Bower]: http://bower.io
+
+{% endmarkdown %}
+{% include alert/end.html%}
+
+PouchDB packages come in three flavors: *presets*, *plugins*, and
+*utilities*.
+
+**Presets** are a collection of plugins, which expose a `PouchDB` object that is ready to be used.
+
+**Plugins** are features that can be added to a `PouchDB` instance using the `PouchDB.plugin()`
+API.
+
+**Utilities** are grab-bags of helper functions, and are only recommended for advanced use cases.
+
+#### Quick links
+
+* [Presets](#presets)
+* [Plugins](#plugins)
+* [Utilities](#utilities)
+
+{% include anchor.html class="h2" title="Presets" hash="presets" %}
+
+Presets export a `PouchDB` object and contain a built-in set of PouchDB
+plugins. You are free to create your own presets, but PouchDB provides a few first-party presets to address common use cases.
+
+### [pouchdb-browser](https://npmjs.org/package/pouchdb-browser)
+
+The `pouchdb-browser` preset contains the version of PouchDB that is designed
+for the browser. In particular, it ships with the IndexedDB adapter
+as its default adapter. It also contains the replication, HTTP, and map/reduce plugins.
+
+Use this preset if you only want to use PouchDB in the browser,
+and don't want to use it in Node.js. (E.g. to avoid installing LevelDB.)
+
+#### Example Usage
+
+```bash
+npm install pouchdb-browser
+```
+
+```js
+const PouchDB = require('pouchdb-browser');
+const db = new PouchDB('mydb');
+```
+
+#### Source code (simplified)
+
+```js
+const PouchDB = require('pouchdb-core')
+ .plugin(require('pouchdb-adapter-idb'))
+ .plugin(require('pouchdb-adapter-http'))
+ .plugin(require('pouchdb-mapreduce'))
+ .plugin(require('pouchdb-replication'));
+```
+
+### [pouchdb-node](https://npmjs.org/package/pouchdb-node)
+
+The `pouchdb-node` preset contains the version of PouchDB that is designed for
+Node.js. In particular, it uses the LevelDB adapter and doesn't ship with the
+IndexedDB or WebSQL adapters. It also contains the replication, HTTP, and map/reduce plugins.
+
+Use this preset if you are only using PouchDB in Node, and not in the browser.
+
+#### Example Usage
+
+```bash
+npm install pouchdb-node
+```
+
+```js
+const PouchDB = require('pouchdb-node');
+const db = new PouchDB('mydb');
+```
+
+#### Source code (simplified)
+
+```js
+const PouchDB = require('pouchdb-core')
+ .plugin(require('pouchdb-adapter-leveldb'))
+ .plugin(require('pouchdb-adapter-http'))
+ .plugin(require('pouchdb-mapreduce'))
+ .plugin(require('pouchdb-replication'));
+```
+
+### [pouchdb-core](https://npmjs.org/package/pouchdb-core)
+
+The `pouchdb-core` package is a special preset in that it exposes the minimum
+number of APIs. It contains zero plugins and is designed to be used in addition
+with other plugins. By itself, it probably isn't very useful.
+
+#### Example Usage
+
+```bash
+npm install pouchdb-core
+```
+
+```js
+const PouchDB = require('pouchdb-core');
+PouchDB.plugin(/* attach plugins to make me more interesting! */);
+```
+
+{% include anchor.html class="h2" title="Plugins" hash="plugins" %}
+
+Plugins contain functionality that can be added to a `PouchDB` instance using `PouchDB.plugin()`. There are many [third-party plugins](/external.html), but the ones described below are first-party plugins, which are given the same level of support as PouchDB itself. Some first-party plugins are included in the default `pouchdb` build, whereas others aren't.
+
+There is also a special type of plugin called an _adapter plugin_. Adapter plugins (such as IndexedDB, WebSQL, LevelDB, and HTTP) determine the storage format that
+PouchDB uses. For the non-HTTP adapters, the plugin order matters, i.e. if you
+want IndexedDB to be preferred to WebSQL, then you should load it first.
+
+### [pouchdb-adapter-idb](https://npmjs.org/package/pouchdb-adapter-idb)
+
+The primary adapter used by PouchDB in the browser, using IndexedDB. The adapter
+name is `'idb'`.
+
+#### Example usage
+
+```bash
+npm install pouchdb-adapter-idb
+```
+
+```js
+PouchDB.plugin(require('pouchdb-adapter-idb'));
+const db = new PouchDB('mydb', {adapter: 'idb'});
+console.log(db.adapter); // 'idb'
+```
+
+### [pouchdb-adapter-websql](https://npmjs.org/package/pouchdb-adapter-websql)
+
+An adapter used by PouchDB in the browser, using WebSQL. The adapter
+name is `'websql'`.
+
+Before PouchDB 7.0.0, this was shipped as a default adapter. As of PouchDB 7.0.0, it must be loaded as a separate plugin.
+
+#### Example usage
+
+```bash
+npm install pouchdb-adapter-websql
+```
+
+```js
+PouchDB.plugin(require('pouchdb-adapter-websql'));
+const db = new PouchDB('mydb', {adapter: 'websql'});
+console.log(db.adapter); // 'websql'
+```
+
+### [pouchdb-adapter-leveldb](https://npmjs.org/package/pouchdb-adapter-leveldb)
+
+{% include alert/start.html variant="warning"%}
+{% markdown %}
+**Warning: deprecation notice.** The `leveldb` adapter will be deprecated in PouchDB version 10.0.0 and removed in version 11.0.0. You can read [the migration guide here](https://pouchdb.com/2026/04/10/migration-to-nodesqlite.html) and more about the topic in [this link](https://github.com/apache/pouchdb/issues/9163).
+{% endmarkdown %}
+{% include alert/end.html%}
+
+The primary adapter used by PouchDB in Node.js, using LevelDB. The adapter name
+is `'leveldb'`.
+
+#### Example usage
+
+```bash
+npm install pouchdb-adapter-leveldb
+```
+
+```js
+PouchDB.plugin(require('pouchdb-adapter-leveldb'));
+const db = new PouchDB('mydb', {adapter: 'leveldb'});
+console.log(db.adapter); // 'leveldb'
+```
+
+### [pouchdb-adapter-http](https://npmjs.org/package/pouchdb-adapter-http)
+
+The primary adapter used by PouchDB in both Node.js and the browser for communicating
+with external CouchDB (or CouchDB-like) servers.
+
+This plugin can be added to PouchDB in any order, and is somewhat special in that
+you must pass in a name like `'http://...'` in order to use it. The adapter name
+is either `'http'` or `'https'` depending on the protocol.
+
+#### Example usage
+
+```bash
+npm install pouchdb-adapter-http
+```
+
+```js
+PouchDB.plugin(require('pouchdb-adapter-http'));
+const db = new PouchDB('http://127.0.0.1:5984/mydb');
+console.log(db.adapter); // 'http'
+```
+
+### [pouchdb-adapter-memory](https://npmjs.org/package/pouchdb-adapter-memory)
+
+{% include alert/start.html variant="warning"%}
+{% markdown %}
+**Warning: deprecation notice.** The `memory` adapter will be deprecated in PouchDB version 10.0.0 and removed in version 11.0.0. You can read [the migration guide here](https://pouchdb.com/2026/04/10/migration-to-nodesqlite.html) and more about the topic in [this link](https://github.com/apache/pouchdb/issues/9163).
+{% endmarkdown %}
+{% include alert/end.html%}
+
+An optional adapter that works in the browser and Node.js, fully in-memory. The adapter name
+is `'memory'`.
+
+#### Example usage
+
+```bash
+npm install pouchdb-adapter-memory
+```
+
+```js
+PouchDB.plugin(require('pouchdb-adapter-memory'));
+const db = new PouchDB('mydb', {adapter: 'memory'});
+console.log(db.adapter); // 'memory'
+```
+
+### [pouchdb-adapter-localstorage](https://npmjs.org/package/pouchdb-adapter-localstorage)
+
+An optional adapter that works in the browser using LocalStorage. The adapter name
+is `'localstorage'`.
+
+#### Example usage
+
+```bash
+npm install pouchdb-adapter-localstorage
+```
+
+```js
+PouchDB.plugin(require('pouchdb-adapter-localstorage'));
+const db = new PouchDB('mydb', {adapter: 'localstorage'});
+console.log(db.adapter); // 'localstorage'
+```
+
+### [pouchdb-adapter-node-websql](https://npmjs.org/package/pouchdb-adapter-node-websql)
+
+An optional adapter that works in Node.js using SQLite via [node-websql](https://github.com/nolanlawson/node-websql). The adapter name
+is `'websql'`.
+
+#### Example usage
+
+```bash
+npm install pouchdb-adapter-node-websql
+```
+
+```js
+PouchDB.plugin(require('pouchdb-adapter-node-websql'));
+const db = new PouchDB('mydb', {adapter: 'websql'});
+console.log(db.adapter); // 'websql'
+```
+
+### [pouchdb-adapter-indexeddb](https://npmjs.org/package/pouchdb-adapter-indexeddb)
+
+{% include alert/start.html variant="warning"%}
+{% markdown %}
+
+**Warning: experimental.** You probably don't want to use this yet. 😉
+
+{% endmarkdown %}
+{% include alert/end.html%}
+
+An experimental next-generation IndexedDB adapter, also known as "idb-next". Currently not shipped as part of PouchDB. The adapter name is `'indexeddb'`.
+
+#### Example usage
+
+```bash
+npm install pouchdb-adapter-indexeddb
+```
+
+```js
+PouchDB.plugin(require('pouchdb-adapter-indexeddb'));
+const db = new PouchDB('mydb', {adapter: 'indexeddb'});
+console.log(db.adapter); // 'indexeddb'
+```
+
+### [pouchdb-find](https://npmjs.org/package/pouchdb-find)
+
+PouchDB's "Mango" query API, exposed via the `find()`, `listIndexes(), `createIndex()`, and `deleteIndex()` methods. Not shipped by default in PouchDB.
+
+#### Example usage
+
+```bash
+npm install pouchdb-find
+```
+
+```js
+PouchDB.plugin(require('pouchdb-find'));
+const db = new PouchDB('mydb');
+db.find(/* see API docs for full info */);
+```
+
+### [pouchdb-mapreduce](https://npmjs.org/package/pouchdb-mapreduce)
+
+PouchDB's map/reduce API, exposed via the `query()` and `viewCleanup()` methods. Ships by default in PouchDB.
+
+#### Example usage
+
+```bash
+npm install pouchdb-mapreduce
+```
+
+```js
+PouchDB.plugin(require('pouchdb-mapreduce'));
+const db = new PouchDB('mydb');
+db.query(/* see query API docs for full info */);
+```
+
+### [pouchdb-replication](https://npmjs.org/package/pouchdb-replication)
+
+PouchDB's replication API, exposed via the `replicate()` and `sync()` methods. Ships by default in PouchDB.
+
+#### Example usage
+
+```bash
+npm install pouchdb-replication
+```
+
+```js
+PouchDB.plugin(require('pouchdb-replication'));
+const db = new PouchDB('mydb');
+db.replicate(/* see replicate/sync API docs for full info */);
+```
+
+{% include anchor.html class="h2" title="Utilities" hash="utilities" %}
+
+These utilities are intended only for advanced users of PouchDB, such as
+third-party plugin authors. Formerly, many of them were exposed via the `extras/` API, which
+is now deprecated.
+
+Most of these are internal, and the APIs are not thoroughly documented. You will
+most likely need to read the source code to understand how they work.
+
+{% include alert/start.html variant="warning"%}
+{% markdown %}
+
+**Warning:** you are entering a semver-free zone.
+
+In contrast to the presets and plugins listed above, **none of the following packages
+follow semver**. Their versions are pinned to PouchDB's, and may change at any time
+without warning. You are strongly recommended to **use exact versions** when installing these packages.
+
+{% endmarkdown %}
+{% include alert/end.html%}
+
+### [pouchdb-abstract-mapreduce](https://npmjs.org/package/pouchdb-abstract-mapreduce)
+
+The underlying logic for secondary indexes, as expressed in both `pouchdb-mapreduce` and `pouchdb-find`. Both packages use this package under the hood.
+
+#### Example usage
+
+```bash
+npm install --save-exact pouchdb-abstract-mapreduce
+```
+
+### [pouchdb-adapter-utils](https://npmjs.org/package/pouchdb-adapter-utils)
+
+Utilities for PouchDB adapters.
+
+#### Example usage
+
+```bash
+npm install --save-exact pouchdb-adapter-utils
+```
+
+### [pouchdb-ajax](https://npmjs.org/package/pouchdb-ajax)
+
+PouchDB's `ajax()` function.
+
+#### Example usage
+
+```bash
+npm install --save-exact pouchdb-ajax
+```
+
+### [pouchdb-binary-utils](https://npmjs.org/package/pouchdb-binary-utils)
+
+Utilities for operating on binary strings and Buffers/Blobs.
+
+#### Example usage
+
+```bash
+npm install --save-exact pouchdb-binary-utils
+```
+
+### [pouchdb-checkpointer](https://npmjs.org/package/pouchdb-checkpointer)
+
+Tool to write a checkpoint, e.g. during replication.
+
+#### Example usage
+
+```bash
+npm install --save-exact pouchdb-checkpointer
+```
+
+### [pouchdb-collate](https://npmjs.org/package/pouchdb-collate)
+
+Collation functions for PouchDB map/reduce.
+
+#### Example usage
+
+```bash
+npm install --save-exact pouchdb-collate
+```
+
+### [pouchdb-collections](https://npmjs.org/package/pouchdb-collections)
+
+ES6 shims for Map and Set as used in PouchDB.
+
+#### Example usage
+
+```bash
+npm install --save-exact pouchdb-collections
+```
+
+### [pouchdb-errors](https://npmjs.org/package/pouchdb-errors)
+
+Errors exposed by PouchDB.
+
+#### Example usage
+
+```bash
+npm install --save-exact pouchdb-errors
+```
+
+### [pouchdb-generate-replication-id](https://npmjs.org/package/pouchdb-generate-replication-id)
+
+Function to generate a replication ID to mark progress during replications.
+
+#### Example usage
+
+```bash
+npm install --save-exact pouchdb-generate-replication-id
+```
+
+### [pouchdb-json](https://npmjs.org/package/pouchdb-json)
+
+Utilities for safely stringifying and parsing JSON.
+
+#### Example usage
+
+```bash
+npm install --save-exact pouchdb-json
+```
+
+### [pouchdb-mapreduce-utils](https://npmjs.org/package/pouchdb-mapreduce-utils)
+
+Utilities used by `pouchdb-mapreduce`.
+
+#### Example usage
+
+```bash
+npm install --save-exact pouchdb-mapreduce-utils
+```
+
+### [pouchdb-md5](https://npmjs.org/package/pouchdb-md5)
+
+Utilities for calculating MD5 checksums.
+
+#### Example usage
+
+```bash
+npm install --save-exact pouchdb-md5
+```
+
+### [pouchdb-merge](https://npmjs.org/package/pouchdb-merge)
+
+PouchDB's CouchDB-style document merge algorithm.
+
+#### Example usage
+
+```bash
+npm install --save-exact pouchdb-merge
+```
+
+### [pouchdb-promise](https://npmjs.org/package/pouchdb-promise)
+
+A `Promise` object, polyfilled using `lie` if Promises aren't available globally.
+
+#### Example usage
+
+```bash
+npm install --save-exact pouchdb-promise
+```
+
+### [pouchdb-selector-core](https://npmjs.org/package/pouchdb-selector-core)
+
+The core Mango selector logic, which allows the selector to be used both by `pouchdb-find` and for filtering/replication.
+
+#### Example usage
+
+```bash
+npm install --save-exact pouchdb-selector-core
+```
+
+### [pouchdb-utils](https://npmjs.org/package/pouchdb-utils)
+
+A potpourri of miscellaneous utilities used by PouchDB and its sub-packages.
+
+#### Example usage
+
+```bash
+npm install --save-exact pouchdb-utils
+```
+
+### [sublevel-pouchdb](https://npmjs.org/package/sublevel-pouchdb)
+
+Fork of [level-sublevel](https://github.com/dominictarr/level-sublevel)
+with only the subset of the API that PouchDB uses.
+
+#### Example usage
+
+```bash
+npm install --save-exact sublevel-pouchdb
+```
diff --git a/docs/version/latest/download.md b/docs/version/latest/download.md
new file mode 100644
index 0000000000..a461e55bc3
--- /dev/null
+++ b/docs/version/latest/download.md
@@ -0,0 +1,75 @@
+---
+layout: 2ColLeft.html
+title: Download
+sidebar: ./nav.html
+---
+
+{% include anchor.html class="h3" title="Quick Start" hash="file" %}
+
+{% highlight html %}
+
+
+{% endhighlight %}
+
+PouchDB can also be directly downloaded:
+
+* [pouchdb-{{ site.version }}.min.js][latest-min] (compressed for production)
+* [pouchdb-{{ site.version }}.js][latest] (uncompressed for debugging)
+
+If you are using PouchDB in Internet Explorer a [Promise](https://www.npmjs.com/package/promise-polyfill) and [Fetch](https://www.npmjs.com/package/whatwg-fetch) polyfill will be needed.
+
+{% include anchor.html class="h3" title="npm" hash="npm" %}
+
+PouchDB can be installed through [npm](https://npmjs.com):
+
+{% highlight bash %}npm install --save pouchdb{% endhighlight %}
+
+After installing, call `require()` to use it:
+
+{% highlight javascript %}
+const PouchDB = require('pouchdb');
+const db = new PouchDB('my_database');
+{% endhighlight %}
+
+PouchDB can be used either in Node or in the browser. A bundler such as [Browserify](https://browserify.org/), [Webpack](https://webpack.github.io/), or [Rollup](https://rollupjs.org/) is needed for browser usage.
+
+#### Browser only
+
+If you're only using PouchDB in the browser, you can use `pouchdb-browser` for
+faster install times:
+
+{% highlight bash %}npm install --save pouchdb-browser{% endhighlight %}
+
+{% highlight javascript %}
+const PouchDB = require('pouchdb-browser');
+const db = new PouchDB('my_database');
+{% endhighlight %}
+
+See [custom builds]({{ site.baseurl }}/custom.html) for more options.
+
+{% include anchor.html class="h3" title="CDNs" hash="cdn" %}
+
+PouchDB is hosted at these CDNs:
+
+* [cdnjs](https://cdnjs.com/libraries/pouchdb)
+* [jsdelivr](https://www.jsdelivr.com/#!pouchdb)
+* [unpkg](https://unpkg.com/pouchdb@{{ site.version }}/dist/)
+
+{% highlight bash %}bower install --save pouchdb{% endhighlight %}
+
+{% include anchor.html class="h3" title="Past releases" hash="past-releases" %}
+
+For past releases and changelog, check out the [Github releases page](https://github.com/apache/pouchdb/releases).
+
+{% include anchor.html class="h3" title="Plugins" hash="plugins" %}
+
+For third-party plugins, see the [plugins page](/external.html).
+
+{% include anchor.html class="h3" title="Custom builds" hash="custom" %}
+
+For custom builds and first-party plugins, see the [custom builds]({{ site.baseurl }}/custom.html) page.
+
+[latest]: https://github.com/apache/pouchdb/releases/download/{{ site.version }}/pouchdb-{{ site.version }}.js
+[latest-min]: https://github.com/apache/pouchdb/releases/download/{{ site.version }}/pouchdb-{{ site.version }}.min.js
diff --git a/docs/version/latest/errors.md b/docs/version/latest/errors.md
new file mode 100644
index 0000000000..9fa2b7ba8e
--- /dev/null
+++ b/docs/version/latest/errors.md
@@ -0,0 +1,321 @@
+---
+layout: 2ColLeft.html
+title: Common Errors
+sidebar: ./nav.html
+---
+
+{% include anchor.html class="h3" title="No `Access-Control-Allow-Origin` header" hash="no_access_control_allow_origin_header" %}
+
+If you see the error:
+
+```bash
+XMLHttpRequest cannot load [...]
+No 'Access-Control-Allow-Origin' header is present on the requested resource.
+Origin [...] is therefore not allowed access.
+```
+
+or this one:
+
+```bash
+Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at http://[couchDBIP]:[couchDBPort]/[dbname]/?_nonce=[request hash]. This can be fixed by moving the resource to the same domain or enabling CORS
+```
+
+it's because you need to enable [CORS](https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS) on CouchDB/Cloudant/whatever you're using. Otherwise, your scripts can only access the server database if they're served from the same origin — the protocol (ex: _http://_, _https://_), domain, and port number must match.
+
+You can enable CORS in CouchDB using `curl` or the Futon web interface, but we've saved you some time by making a Node script called [add-cors-to-couchdb](https://github.com/pouchdb/add-cors-to-couchdb). Just run:
+
+{% highlight bash %}
+$ npm install -g add-cors-to-couchdb
+$ add-cors-to-couchdb
+{% endhighlight %}
+
+Or if your database is not at `127.0.0.1:5984`:
+
+{% highlight bash %}
+$ add-cors-to-couchdb http://me.example.com \
+ -u myusername -p mypassword
+{% endhighlight %}
+
+You can check that CORS is now enabled by visiting [http://localhost:5984/_utils/config.html](http://localhost:5984/_utils/config.html) in your browser. You should see something like this:
+
+{% include img.html src="cors_in_couchdb.png" alt="CORS settings in CouchDB" %}
+
+{% include anchor.html class="h3" title="PouchDB throws a `No valid adapter found` error" hash="no_valid_adapter" %}
+
+Reading from/writing to a local database from an `iframe` with a different origin will cause PouchDB to throw a `No valid adapter found` error in Firefox. This is due to Firefox's IndexedDB implementation.
+
+IndexedDB has a same-origin restriction. Read/write operations from another origin will always fail, but only Firefox triggers a `No valid adapter found` error. Chrome / Opera will instead throw an [`UnknownError`](#unknown_error_chrome).
+
+{% include anchor.html class="h3" title="iOS/Safari: \"there was not enough remaining storage space\"" hash="not_enough_space" %}
+
+On iOS and Safari, if you expect your app to use more than 5MB of space, you will need to request the space up-front from the user. In certain versions of Safari, you can never request more than what the user originally grants you.
+
+{% include img.html src="safari_popup.png" alt="Safari storage quota popup" %}
+
+To get around this, when you create your PouchDB, use the `opts.size` option for the expected _maximum_ size of the database in MB. Valid increments are 10, 50, 100, 500, and 1000. For instance, if you request 50, then Safari will show a popup saying "allow 50MB?" but if you request 51, then Safari will show a popup saying "allow 100MB?".
+
+If you don't use the `size` option, then you'll be able to use up to 5MB without any popup, but then once you use more, there will be a popup asking for 10.
+
+```js
+new PouchDB('mydb', {size: 10}); // request 10 MB with a popup
+new PouchDB('mydb', {size: 50}); // request 50 MB with a popup
+new PouchDB('mydb'); // implicitly request 5 MB, no popup until you exceed 5MB
+```
+
+This does not affect any backend other than Web SQL. Chrome, Android, and Opera do not show the popup. On PhoneGap/Cordova apps, you can also use the [SQLite plugin][sqlite] to get around this problem. Here's [more information about storage quotas](http://www.html5rocks.com/en/tutorials/offline/quota-research) and [details on the Safari/iOS bug](https://github.com/apache/pouchdb/issues/2347).
+
+{% include anchor.html class="h3" title="PouchDB throws 404 (Object Not Found) for '_local' document" hash="404__local_document" %}
+
+Don't worry, nothing is amiss, this is expected behaviour:
+During PouchDB's initial replication PouchDB will check for a checkpoint, if it doesn't exist a 404 will be returned and a checkpoint will subsequently be written.
+
+{% include anchor.html class="h3" title="PouchDB is throwing `InvalidStateError`" hash="throwing_invalidstateerror" %}
+
+Are you in private browsing mode? IndexedDB is [disabled in private browsing mode](https://developer.mozilla.org/en-US/docs/IndexedDB/Using_IndexedDB) in Firefox.
+
+{% include anchor.html class="h3" title="Can't open second database on Android WebView with Cordova/Phone Gap" hash="phonegap_cordova_second_database" %}
+
+There is a limit of one database per app in some versions of the Android WebView. Install the [SQLite plugin][sqlite], then PouchDB will use that if it is available.
+
+{% include anchor.html class="h3" title="Possible EventEmitter memory leak detected" hash="event_emitter_limit" %}
+
+If you see this warning:
+
+```bash
+(node) warning: possible EventEmitter memory leak detected. 11 listeners added.
+Use emitter.setMaxListeners() to increase limit.
+```
+
+This is because PouchDB uses Node-style [EventEmitters](https://nodejs.org/api/events.html) for its events. An EventEmitter is any object that has an `.on()` or `once()` method, such as `db.changes().on('change', ...`.
+
+By default, all EventEmitters have 10 listeners, and if you exceed that limit, e.g. by attaching many `changes()` listeners or running many simultaneous `replicate()` or `sync()` events, then you may exceed this limit.
+
+**This could indicate a memory leak in your code**. Check to make sure that you are calling `cancel()` on any `changes()`, `replicate()`, or `sync()` handlers, if you are constantly starting and stopping those events.
+
+If you're sure it's not a memory leak, though, you can increase the limit by doing:
+
+{% highlight javascript %}
+db.setMaxListeners(20); // or 30 or 40 or however many you need
+{% endhighlight %}
+
+In the above example, `db` refers to a database object you created using `new PouchDB('dbname')`.
+
+{% include anchor.html class="h3" title="Database size limitation of ~5MB on iOS with Cordova/Phone Gap" hash="size_limitation_5mb" %}
+
+If you're storing large amounts of data, such as PNG attachments, the [SQLite plugin][sqlite] is again your friend. (See [issue #1260](https://github.com/apache/pouchdb/issues/1260) for details.)
+
+{% include anchor.html class="h3" title="CouchDB returns a 404 for GETs from a CouchApp" hash="404_get_couchapp" %}
+
+Certain URL rewrites are broken by PouchDB's cache-busting; try adding `{cache : false}` to the PouchDB constructor. (See [issue #1233](https://github.com/apache/pouchdb/issues/1233) for details.)
+
+{% include anchor.html class="h3" title="Uncaught TypeError: object is not a function" hash="typeerror_object_is_not_a_function" %}
+
+Did you include the [es6-promise shim library](https://github.com/jakearchibald/es6-promise)? Not every browser implements ES6 Promises correctly. (See [issue #1747](https://github.com/apache/pouchdb/issues/1747) for details.)
+
+{% include anchor.html class="h3" title="SyntaxError: Parse error (Cordova on Android)" hash="cordova_android_parse_error" %}
+
+Did you include the [es5-shim library][es5shim]? PouchDB is written in ES5, which is supported by modern browsers, but requires shims for older browsers (e.g. IE 9, Android 2.1 WebView).
+
+In Android, if you're loading PouchDB directly via `webView.loadUrl('javascript://' + js')`, you should prefer the minified PouchDB javascript file to the unminified one, since code comments can also cause parse errors.
+
+{% include anchor.html class="h3" title="PouchDB object fails silently (Safari)" hash="safari_object_silent_fail" %}
+
+Safari requires users to confirm that they want to allow an app to store data locally ("Allow this website to use space on your disk?"). If PouchDB is loaded in an `iframe` or some other unusual way, the dialog might not be shown, and the database will silently fail.
+
+{% include anchor.html class="h3" title="window.localStorage is not available (Chrome apps)" hash="window_localstorage_chrome_apps" %}
+
+In Chrome apps, you'll see the warning "window.localStorage is not available in packaged apps. Use chrome.storage.local instead." This is harmless; since PouchDB doesn't use localStorage if it's not available.
+
+{% include anchor.html class="h3" title="Error: UnknownError (Firefox)" hash="unknown_error_ff" %}
+
+Are you using a webserver to host and run your code? This error can be caused by running your script/file locally using the `file:///` setting in Firefox, since Firefox does not [allow access to IndexedDB locally](https://bugzilla.mozilla.org/show_bug.cgi?id=643318). You can use the SimpleHTTPServer to deploy your script by running `python -m SimpleHTTPServer` from the directory containing the script, or use the Apache webserver and then access the script by using `http://localhost/{path_to_your_script}`.
+
+{% include anchor.html class="h3" title="Error: UnknownError (Chrome / Opera)" hash="unknown_error_chrome" %}
+
+This can occur when attempting to read from or write to IndexedDB from a different origin. IndexedDB has a same-origin restriction. Attempting to write to the database associated with _http://example.com_ from an `iframe` served from _http://api.example.com_, for example, will fail.
+
+In Firefox, PouchDB instead throws a [`No valid adapter found`](#no_valid_adapter) error.
+
+{% include anchor.html class="h3" title="DataCloneError: An object could not be cloned" hash="could_not_be_cloned" %}
+
+If you ever see:
+
+```bash
+Uncaught DataCloneError:
+ Failed to execute 'put' on 'IDBObjectStore':
+ An object could not be cloned.
+```
+
+Or:
+
+```bash
+DataCloneError: The object could not be cloned.
+```
+
+Then the problem is that the document you are trying to store is not a pure JSON object. For example, an object with its own class (`new Foo()`) or with special methods like getters and setters cannot be stored in PouchDB/CouchDB.
+
+If you are ever unsure, then run this on the document:
+
+```js
+JSON.parse(JSON.stringify(myDocument));
+```
+
+If the object you get out is the same as the object you put in, then you are storing the right kind of object.
+
+Note that this also means that you cannot store `Date`s in your document. You must convert them to strings or numbers first. `Date`s will be stored as-is in IndexedDB, but in the other adapters and in CouchDB, they will be automatically converted to ISO string format, e.g. `'2015-01-01T12:00:00.000Z'`. This can caused unwanted results. See [#2351](https://github.com/apache/pouchdb/issues/2351) and [#2158](https://github.com/apache/pouchdb/issues/2158) for details.
+
+{% include anchor.html class="h3" title="DOM Exception 18 in Android pre-Kitkat WebView" hash="android_pre_kitkat" %}
+
+This applies to hybrid apps designed to run in Android pre-Kitkat (i.e. before 4.4).
+
+If you are directly using a `WebView` and not using Cordova/PhoneGap, you will probably either run into an error where PouchDB silently fails or you see `Error: SECURITY_ERR: DOM Exception 18` in the console. As a sanity test, you can run this JavaScript:
+
+```js
+openDatabase('mydatabase', 1, 'mydatabase', 5000000, function (db) { console.log('it works!'); });
+```
+
+If you see "it works" in the console, then everything's peachy. Otherwise there are a few things you have to do.
+
+First, make sure Web SQL is enabled on your `WebView` in the first place using [setDatabaseEnabled](http://developer.android.com/reference/android/webkit/WebSettings.html#setDatabaseEnabled%28boolean%29):
+
+```java
+myWebView.getSettings().setDatabaseEnabled(true);
+```
+
+Second, specify a path for the database. Yes, you need to do this, even though it's deprecated in Kitkat:
+
+```java
+String databasePath = getContext().getApplicationContext().getDir(
+ "database", Context.MODE_PRIVATE).getPath();
+webView.getSettings().setDatabasePath(databasePath);
+```
+
+Third, you'll need to set an `onExceededDatabaseQuota` handler. Yes, it's also deprecated in Kitkat. Yes, you still need to do it.
+
+```java
+webView.setWebChromeClient(new WebChromeClient() {
+
+ @Override
+ public void onExceededDatabaseQuota(String url, String databaseIdentifier, long currentQuota, long estimatedSize,
+ long totalUsedQuota, WebStorage.QuotaUpdater quotaUpdater) {
+ quotaUpdater.updateQuota(estimatedSize * 2);
+ }
+});
+```
+
+If you skip any one of these three steps, then you will get the `DOM Exception 18` error. You need to do all three.
+
+Alternatively, you can also load the `WebView` with a fake `http://` URL, but this may cause other errors when you try to fetch files based on a relative path:
+
+```java
+webView.loadDataWithBaseURL("http://www.example.com",
+ htmlContent,
+ "text/html",
+ "utf-8",
+ null);
+```
+
+{% include anchor.html class="h3" title="Replication with attachments is slow or fails" hash="replicating_attachments_slow" %}
+
+The symptoms for this issues are:
+
+1. Replicating a database that has many attachments from a CouchDB server is either slow or fails randomly.
+2. You get server error message of the nature `No buffer space available`.
+
+Chances are that your server runs inside a virtual machine. The host system, or hypervisor, imposes limits on how much data each virtual machine can use for networking. If you are on a cheap virtual server, it is possible, that the default settings for PouchDB pull-replication (10 parallel batches of 100 documents each) exhaust the narrow limit of your server. Even a single client can cause this.
+
+The solution is to move to a better server, but if that is not an immediate option, a workaround would be reducing the `options.batch_size` and `options.batches_limit` [replication options]({{ site.baseurl }}/api.html#replication).
+
+To find optimal values, start by setting them both to 1 (meaning that PouchDB should download one document after the other) and increase from there and stop when the symptoms begin again. Note that multiple concurrent clients can still cause an issue, if you get too many. If all your documents have one or more attachments (e.g. a photos database), setting both options to `1` is probably a good idea.
+
+{% include alert/start.html variant="info" %}
+
+Generally, reducing these options that replicating the database down will take more time. Please test various settings to see what works for you and your hardware.
+
+{% include alert/end.html %}
+
+{% include alert/start.html variant="info" %}
+
+This issue has been found on OpenVZ systems, but other Hypervisors might also be affected. See
+[http://blog.aplikacja.info/105_no_buffer_space_available_on_openvz_vps.html](http://blog.aplikacja.info/105_no_buffer_space_available_on_openvz_vps.html)
+on how to diagnose this issue.
+
+{% include alert/end.html %}
+
+[es5shim]: https://github.com/es-shims/es5-shim
+[sqlite]: https://github.com/brodysoft/Cordova-SQLitePlugin
+
+{% include anchor.html class="h3" title="Packaging PouchDB in an app with WebPack" hash="package_pouchdb_webpack" %}
+
+PouchDB may have various dependencies that may not play nicely with WebPack. Here are some issues you may run into and their resolutions:
+
+**You may need an appropriate loader to handle this file type.**
+
+If you run into the following error (or similar):
+
+```bash
+ERROR in ./~/pouchdb/~/levelup/package.json
+Module parse failed: /path/to/node_modules/pouchdb/node_modules/levelup/package.json Line 2: Unexpected token :
+You may need an appropriate loader to handle this file type.
+| {
+| "name": "levelup",
+| "description": "Fast & simple storage - a Node.js-style LevelDB wrapper",
+| "version": "0.18.6",
+ @ ./~/pouchdb/~/levelup/lib/util.js 102:30-56
+
+```
+WebPack needs to be configured to recognize how to load json files. Simply, install `json-loader` and edit `webpack.config.js` as follows:
+
+```js
+module: {
+ loaders: [
+ // https://github.com/apache/pouchdb/issues/3319
+ {
+ test: /\.json$/,
+ loader: "json-loader"
+ }
+ ]
+}
+```
+
+{% include anchor.html class="h3" title="Failed to load resource: the server responded with a status of 400 (Bad request) " hash="couchbase_dbname" %}
+
+If you are using Couchbase Lite to sync with PouchDB then you cannot use capital letters in your database name as Couchbase Lite has restrictions on valid database names.
+
+{% include anchor.html class="h3" title="Live changes pass undetected (Web Extension)" hash="webextension_live_changes" %}
+
+When using PouchDB in a WebExtension (at least in Chromium 55 and Firefox 50), but apparently only if the `manifest.json` contains the `store` permission, a [live changes feed]({{ site.baseurl }}/guides/changes.html#live-changes-feed) in one tab or background process may not detect changes made in another tab/process. It will of course still report those changes upon the next change that it does detect.
+Minimal code to reproduce this can be found [here](https://gist.github.com/Treora/150dca4b57b1b881bd049303e82c5ced).
+
+{% include anchor.html class="h3" title="PouchDB install errors on Windows" hash="pouchdb_install_errors_windows" %}
+
+Sometimes `npm install pouchdb` fails on windows, when no prebuilt binary is available. The error looks similar to this:
+
+```bash
+C:\XXX\node_modules\project_name>if not defined npm_config_node_gyp (node "C:\XXX\node_modules\npm\bin\node-gyp-bin\....\node_modules\node-gyp\bin\node-gyp.js" rebuild ) else (node "" rebuild )
+gyp ERR! configure error
+gyp ERR! stack Error: Can't find Python executable "python", you can set the PYTHON env variable.
+gyp ERR! stack at PythonFinder.failNoPython (C:\XXX\nodejs\node_modules\npm\node_modules\node-gyp\lib\configure.js:483:19)
+```
+
+or something like this
+
+```bash
+gyp ERR! configure error
+npm ERR! code ELIFECYCLE
+npm ERR! errno 1
+npm ERR! leveldown@3.0.0 install: `prebuild-install || node-gyp rebuild`
+npm ERR! Exit status 1
+npm ERR!
+npm ERR! Failed at the leveldown@3.0.0 install script.
+```
+
+If you are on windows and getting any error messages mentioning **leveldown** and **Python**, please attempt the steps below.
+
+Node-Gyp requires Python 2.7, it does not work with Python 3.x unfortunately.
+
+Here are a few steps you can take:
+
+1. try `npm install --global --production windows-build-tools` (this command needs to be ran with admin privileges)
+2. read other [pointers to using node-gyp on Windows here](https://github.com/nodejs/node-gyp#on-windows)
+3. If you are only using PouchDB in the browser, you can install [pouchdb-browser](https://www.npmjs.com/package/pouchdb-browser) instead: `npm install --save pouchdb-browser`
diff --git a/docs/version/latest/external.md b/docs/version/latest/external.md
new file mode 100644
index 0000000000..95cbc41816
--- /dev/null
+++ b/docs/version/latest/external.md
@@ -0,0 +1,306 @@
+---
+layout: 2ColLeft.html
+title: Plugins and External Projects
+sidebar: ./nav.html
+---
+
+Below is a list of known plugins, tools and projects can be used with PouchDB.
+
+## {% include anchor.html title="Plugins" hash="plugins" %}
+
+#### [PouchDB allDbs()](https://github.com/nolanlawson/pouchdb-all-dbs)
+
+Revives the `allDbs()` function, which lists all PouchDB databases.
+
+#### [PouchDB Authentication](https://github.com/nolanlawson/pouchdb-authentication)
+
+Plugin for CouchDB's authentication system.
+
+#### [Pouch Box](https://github.com/jo/pouch-box)
+
+Allows decentralized authentication and access control per document, using asymmetric encryption.
+
+#### [PouchDB Collate](https://github.com/apache/pouchdb/tree/master/packages/node_modules/pouchdb-collate)
+
+Collation functions for PouchDB map/reduce. Used by PouchDB map/reduce to maintain consistent [CouchDB collation ordering](https://wiki.apache.org/couchdb/View_collation).
+
+#### [Crypto Pouch](https://github.com/calvinmetcalf/crypto-pouch)
+
+Encrypt a PouchDB/CouchDB database.
+
+#### [Pouch Dat](https://github.com/calvinmetcalf/pouch-dat)
+
+Replicate from PouchDB to Dat.
+
+#### [PouchDB Hyperbee](https://github.com/RangerMauve/pouchdb-adapter-hyperbee)
+
+Sparsely load data from Hyperbee over p2p networks in Node and the Browser.
+
+#### [Pouch Datalog](https://github.com/dahjelle/pouch-datalog)
+
+Implement the Datalog query language on PouchDB, with indexes.
+
+#### [PouchDB Dump](https://github.com/nolanlawson/pouchdb-dump-cli) and [PouchDB Load](https://github.com/nolanlawson/pouchdb-load)
+
+Dump a PouchDB/CouchDB to a file, then load it wholesale. Designed for fast initial replication.
+
+#### [Delta Pouch](https://github.com/redgeoff/delta-pouch)
+
+Implements the handy "every document is a delta" pattern, so you don't have to deal with conflicts.
+
+#### [PouchDB Erase](https://github.com/marten-de-vries/pouchdb-erase)
+
+A replicating `db.destroy()` alternative.
+
+#### [PouchDB Full Sync](https://github.com/nolanlawson/pouchdb-full-sync)
+
+Fully replicate two PouchDB/CouchDB databases, preserving absolutely all revision history.
+
+#### [PouchDB GQL](https://github.com/pouchdb/GQL)
+
+Google Query Language (GQL) queries with PouchDB.
+
+#### [PouchDB Hoodie API](https://github.com/hoodiehq/pouchdb-hoodie-api)
+
+Hoodie-like API for PouchDB. ([Documentation](http://hoodiehq.github.io/pouchdb-hoodie-api/))
+
+#### [PouchDB Hoodie Store Client](https://www.npmjs.com/package/@hoodie/store-client)
+
+PouchDB Hoodie-like API for data persistence & offline sync.
+
+#### [PouchDB List](http://python-pouchdb.marten-de-vries.nl/plugins.html)
+
+Allows you to re-use your CouchDB list functions on the client side. ([Documentation](http://pythonhosted.org/Python-PouchDB/js-plugins.html#pouchdb-rewrite-plug-in))
+
+#### [Pouch Mirror](https://github.com/colinskow/pouch-mirror)
+
+Creates a synced in-memory mirror of a remote CouchDB for faster reads.
+
+#### [PouchDB No-Eval Map/Reduce](https://github.com/evidenceprime/pouchdb.mapreduce.noeval)
+
+Allows you to use the `query()` API in environments that disallow `eval()`, like Chrome packaged apps.
+
+#### [Peer Pouch](https://github.com/natevw/PeerPouch)
+
+PouchDB over WebRTC. (Note: only works with PouchDB 1.1.)
+
+#### [PouchDB Resolve Conflicts](https://github.com/jo/pouch-resolve-conflicts)
+
+Plugin to assist in PouchDB conflict resolving.
+
+#### [PouchDB Migrate](https://github.com/eHealthAfrica/pouchdb-migrate)
+
+PouchDB plugin for running data migrations.
+
+#### [PouchDB Quick Search](https://github.com/nolanlawson/pouchdb-quick-search)
+
+Full-text search engine on top of PouchDB.
+
+#### [Relational Pouch](https://github.com/nolanlawson/relational-pouch)
+
+A relational database API on top of PouchDB/CouchDB.
+
+#### [PouchDB Replication Stream](https://github.com/nolanlawson/pouchdb-replication-stream)
+
+Replicate between CouchDB/PouchDB using streams.
+
+#### [PouchDB Rewrite](http://python-pouchdb.marten-de-vries.nl/plugins.html)
+
+A PouchDB plugin that allows you to re-use your CouchDB rewrites on the client side. ([Documentation](http://pythonhosted.org/Python-PouchDB/js-plugins.html#pouchdb-list-plug-in))
+
+#### [PouchDB Show](http://python-pouchdb.marten-de-vries.nl/plugins.html)
+
+A PouchDB plugin that allows you to re-use your CouchDB show functions on the client side. ([Documentation](http://pythonhosted.org/Python-PouchDB/js-plugins.html#pouchdb-show-plug-in))
+
+#### [SocketPouch](https://github.com/nolanlawson/socket-pouch)
+
+PouchDB/CouchDB replication over WebSockets, using Engine.io (Socket.io).
+
+#### [PouchDB Spatial](https://github.com/pouchdb/geopouch)
+
+Multidimensional and spatial queries with PouchDB.
+
+#### [PouchDB Geospatial](https://github.com/dpmcmlxxvi/pouchdb-geospatial)
+
+PouchDB geospatial querying of GeoJSON objects that supports the DE-9IM spatial predicates. ([Documentation](https://dpmcmlxxvi.github.io/pouchdb-geospatial/api/))
+
+#### [Superlogin](https://www.npmjs.com/package/superlogin)
+
+Powerful authentication for APIs and single page apps using the CouchDB ecosystem, which supports a variety of providers.
+
+#### [Store.PouchDB](https://github.com/chunksnbits/store.pouchdb)
+
+ORM-style storage plugin for PouchDB.
+
+#### [Pouch Stream](https://github.com/calvinmetcalf/PouchStream)
+
+A plugin to let PouchDB talk streams.
+
+#### [Transform Pouch](https://github.com/nolanlawson/transform-pouch)
+
+Transforms documents before and after storage, e.g. for encryption, compression, or massaging data.
+
+
+#### [PouchDB Update](http://python-pouchdb.marten-de-vries.nl/plugins.html)
+
+A PouchDB plugin that allows you to re-use your CouchDB update functions on the client side. ([Documentation](http://pythonhosted.org/Python-PouchDB/js-plugins.html#pouchdb-update-plug-in))
+
+#### [PouchDB Upsert](https://github.com/nolanlawson/pouchdb-upsert)
+
+Convenience functions for working with documents: `upsert()` and `putIfNotExists()`.
+
+#### [PouchDB Validation](http://python-pouchdb.marten-de-vries.nl/plugins.html)
+
+A PouchDB plugin that allows you to re-use your CouchDB `validate_doc_update` functions on the client side. ([Documentation](http://pythonhosted.org/Python-PouchDB/js-plugins.html#pouchdb-validation-plug-in))
+
+#### [WorkerPouch](http://github.com/nolanlawson/worker-pouch)
+
+PouchDB adapter for web workers, so that PouchDB blocks the DOM less.
+
+{% include anchor.html title="Server Side" hash="Server Side" %}
+
+#### [PouchDB Server](https://github.com/pouchdb/pouchdb-server)
+
+A standalone CouchDB-style REST interface server to PouchDB.
+
+#### [Express PouchDB](https://github.com/pouchdb/express-pouchdb)
+
+An Express submodule with a CouchDB-style REST interface to PouchDB. Powers PouchDB Server.
+
+#### [Express PouchDB Replication Stream](https://github.com/conor-mac-aoidh/express-pouchdb-replication-stream)
+
+Server-side Express endpoint to deliver a stream from [PouchDB Replication Stream](https://github.com/nolanlawson/pouchdb-replication-stream).
+
+#### [Howler](https://github.com/redgeoff/couchdb-howler)
+
+Use web sockets to subscribe to CouchDB global changes
+
+#### [Pouch Websocket Sync](https://github.com/pgte/pouch-websocket-sync)
+
+Sync several PouchDBs through websockets. Supports reconnection, negotiation and authentication.
+
+#### [Pouch Remote Stream](https://github.com/pgte/pouch-remote-stream#readme)
+
+Consume a remote PouchDB stream. Goes well with [pouch-stream-server](https://github.com/pgte/pouch-stream-server#readme) on the server side.
+
+#### [Pouch Stream Server](https://github.com/pgte/pouch-stream-server#readme)
+
+PouchDB stream server. Serves generic PouchDB object streams. Goes well with [pouch-remote-stream](https://github.com/pgte/pouch-remote-stream#readme) on the client.
+
+#### [Spiegel](https://github.com/redgeoff/spiegel)
+
+Scalable replication and change listening for CouchDB
+
+{% include anchor.html title="Framework adapters" hash="framework_adapters" %}
+
+### Angular
+
+#### [angular-pouchdb](https://github.com/angular-pouchdb/angular-pouchdb)
+
+Wrapper for using PouchDB within Angular.js.
+
+#### [Factoryng - AngularJS Adapter](https://github.com/redgeoff/factoryng)
+
+An all-in-one AngularJS factory that wraps PouchDB and Delta Pouch.
+
+#### [ngPouch](https://github.com/jrhicks/ngPouch)
+
+Angular service to persist remote connection settings and maintain continuous replication.
+
+#### [ng-pouchdb](https://github.com/danielzen/ng-pouchdb)
+
+AngularJS binding for PouchDB.
+
+### Ampersand
+
+#### [ampersand-collection-pouchdb-mixin](https://github.com/svnlto/ampersand-collection-pouchdb-mixin)
+
+A mixin for extending ampersand-collection with pouchdb persistence.
+
+### Backbone
+
+#### [Backbone PouchDB](https://github.com/jo/backbone-pouch)
+
+Backbone PouchDB Sync Adapter.
+
+### Ember
+
+#### [Ember Pouch](https://github.com/nolanlawson/ember-pouch)
+
+Ember Data adapter for PouchDB/CouchDB.
+
+#### [ember-pouchdb](https://github.com/taras/ember-pouchdb)
+
+Promisy PouchDB wrapper for Ember.js.
+
+### [GopherJS](https://github.com/gopherjs/gopherjs)
+
+#### [Kivik](https://github.com/go-kivik/kivik)
+
+Kivik provides a common interface to CouchDB or CouchDB-like databases for Go and GopherJS. ([PouchDB driver](https://github.com/go-kivik/pouchdb))
+
+### Kendo UI
+
+#### [kendo-pouchdb](https://github.com/terikon/kendo-pouchdb)
+
+Kendo UI DataSource adapter.
+
+### React/Flux
+
+#### [react-pouchdb](https://github.com/ArnoSaine/react-pouchdb)
+
+React wrapper for PouchDB that also subscribes to changes.
+
+#### [pouch-redux](https://github.com/UXtemple/pouch-redux)
+
+Pouch and Redux integration. With Pouch in control this time around.
+
+#### [redux-pouchdb](https://github.com/vicentedealencar/redux-pouchdb)
+
+Sync store state to PouchDB.
+
+#### [redux-pouch](https://github.com/UXtemple/redux-pouch)
+
+PouchDB-backed Redux.
+
+#### [pouch-redux-middleware](https://github.com/pgte/pouch-redux-middleware#readme)
+
+Redux middleware to sync a PouchDB database with the Redux state.
+
+### Vue.js
+
+#### [pouch-vue](https://github.com/MDSLKTR/pouch-vue)
+
+Syncs PouchDB data with Vue.js components using Mango Selectors
+
+#### [vue-pouch-db](https://github.com/QurateInc/vue-pouch-db)
+
+Vue Pouch DB is a VueJS Plugin that binds PouchDB with Vue and keeps a synchronised state with the database. Has support for Mango queries which are processed locally within the VuePouchDB state.
+
+
+{% include anchor.html title="Other languages" hash="Other languages" %}
+
+#### [Python-PouchDB](http://python-pouchdb.marten-de-vries.nl/)
+A Python interface to PouchDB, with both a synchronous and an asynchronous API. Uses QtWebKit internally (via either PySide, PyQt4 or PyQt5). Some PouchDB plugins are also wrapped. ([Documentation](http://pythonhosted.org/Python-PouchDB/) / [Launchpad](https://launchpad.net/python-pouchdb))
+
+#### [PouchDroid](https://github.com/nolanlawson/PouchDroid/)
+
+Android adapter with a native Java interface to PouchDB.
+
+{% include anchor.html title="Tools" hash="Tools" %}
+
+#### [blob-util](https://github.com/nolanlawson/blob-util)
+
+Shims and utils for working with binary Blobs in the browser.
+
+#### [Pouchy](https://www.npmjs.com/package/pouchy)
+
+PouchDB sugar API. ([Github](https://github.com/cdaringe/pouchy))
+
+#### [Puton](http://puton.jit.su/)
+
+A bookmarklet for inspecting PouchDB databases within the browser. ([Github](http://github.com/ymichael/puton))
+
+#### [Revision Tree Visualizer](http://neojski.github.io/visualizeRevTree)
+
+A tool drawing revision tree of a couchdb document. You can see what is a conflict, which revisions are deleted and which is winning. ([Github](https://github.com/neojski/visualizeRevTree))
diff --git a/docs/version/latest/faq.md b/docs/version/latest/faq.md
new file mode 100644
index 0000000000..411ed8fc09
--- /dev/null
+++ b/docs/version/latest/faq.md
@@ -0,0 +1,147 @@
+---
+layout: 2ColLeft.html
+title: FAQ
+sidebar: ./nav.html
+---
+
+{% include anchor.html class="h3" title="Can PouchDB sync with MongoDB/MySQL/my current non-CouchDB database?" hash="sync_non_couchdb" %}
+
+
+No, your backend needs to speak the [CouchDB replication protocol](http://couchdb.readthedocs.org/en/latest/replication/protocol.html). The magic of PouchDB <–> CouchDB sync comes from this design, which in particular requires all documents to be versioned with the `_rev` marker. This allows PouchDB and CouchDB to [elegantly handle conflicts](http://writing.jan.io/2013/12/19/understanding-couchdb-conflicts.html), among other benefits.
+
+{% include anchor.html class="h3" title="What can PouchDB sync with?" hash="what_can_pouchdb_sync_with" %}
+
+There are a number of databases that implement a CouchDB-like protocol, and PouchDB should be able to replicate with them. They include:
+
+ * [CouchDB](http://couchdb.apache.org/) – CouchDB is our primary reference database and is used for automated testing.
+ * [Cloudant](https://cloudant.com/) – A cluster-aware fork of CouchDB.
+ * [PouchDB Server](https://github.com/pouchdb/pouchdb-server) – An HTTP API written on top of PouchDB. Additionally, it supports alternate backends like in-memory, Redis, Riak and MySQL via [the LevelUP ecosystem](https://github.com/rvagg/node-levelup/wiki/Modules#storage). Note that your application must use the PouchDB API rather than directly modifying the database, however.
+
+{% include anchor.html class="h3" title="The web is nice, but I want to build a native app?" hash="native_support" %}
+
+PouchDB is one of multiple projects that implement the CouchDB protocol, and these can all be used to sync the same set of data.
+
+For desktop applications, you may want to look into embedding CouchDB (or [rcouch](https://github.com/refuge/rcouch)). PouchDB also works great with web-based frameworks like [node-webkit](https://github.com/rogerwang/node-webkit), [Chrome apps](https://developer.chrome.com/apps/about_apps), [Electron](https://github.com/atom/electron) and [WinJS](http://try.buildwinjs.com/#listview).
+
+For mobile applications, you can use PouchDB within [PhoneGap](http://phonegap.com/)/[Cordova](http://cordova.apache.org/) (optionally using the [SQLite Plugin](https://github.com/brodysoft/Cordova-SQLitePlugin)), or there are several native libraries:
+
+**iOS**:
+
+* [Couchbase Lite for iOS](https://github.com/couchbase/couchbase-lite-ios)
+* [Cloudant Sync for iOS](https://github.com/cloudant/CDTDatastore)
+
+**Android**:
+
+* [Couchbase Lite for Android](https://github.com/couchbase/couchbase-lite-android)
+* [Cloudant Sync for Android](https://github.com/cloudant/sync-android)
+
+{% include anchor.html class="h3" title="How much data can PouchDB store?" hash="data_limits" %}
+
+In **Firefox**, PouchDB uses IndexedDB. Though Firefox has no upper limit besides disk space, if your application wishes to store more than 50MB locally, Firefox will [ask the user](https://developer.mozilla.org/en-US/docs/Web/API/IndexedDB_API) using a non-modal dialog to confirm that this is okay.
+
+**Chrome** also uses IndexedDB, and it determines the amount of storage available on the user’s hard drive and uses that [to calculate a limit](https://developers.google.com/chrome/whitepapers/storage#temporary).
+
+**Opera 15+** shares a codebase with Chromium/Blink, and behaves similarly.
+
+**Internet Explorer 10+** has a hard 250MB limit, and will prompt the user with a non-modal dialog at 10MB.
+
+**Mobile Safari** on iOS has a hard 50MB limit for WebSQL, whereas **desktop Safari** has no limit. Both will prompt the user with a modal dialog if an application requests more than 5MB of data, at increments of 5MB, 10MB, 50MB, 100MB, etc. Some versions of Safari have a bug where they only let you request additional storage once, so you'll need to request the desired space up-front. PouchDB allows you to do this using [the `size` option]({{ site.baseurl }}/api.html#create_database).
+
+**iOS Web Application**, a page saved on the homescreen behaves differently than apps in Mobile Safari (at least from iOS 9.3.2+). No specifics are published online by Apple, but WebSQL storage seems not limited to 50mb and there will not be any prompts when requesting data storage. Use [``](https://developer.apple.com/library/ios/documentation/AppleApplications/Reference/SafariWebContent/ConfiguringWebApplications/ConfiguringWebApplications.html#//apple_ref/doc/uid/TP40002051-CH3-SW3) in your html header and use *Add to Home Screen* in the share menu of Safari. Please note, IndexedDB is now available as of iOS 10.3. It seems there is different behavior for different models of iPad and iPhone. You can check your mileage using the [storage abuser](http://demo.agektmr.com/storage/), which you can *Add to Home Screen* on your device. **Caveat**: when iOS is running low on storage space, the OS might decide to delete all data without any notice or warning to the end user. Be sure to use devices with plenty of spare space, or your users will lose unsynced data.
+
+**Android** works the same as Chrome as of 4.4+ (IndexedDB), while older versions can store up to 200MB (WebSQL).
+
+In [PhoneGap](http://phonegap.com/)/[Cordova](http://cordova.apache.org/), you can have unlimited data on both iOS and Android by using the [SQLite Plugin](https://github.com/brodysoft/Cordova-SQLitePlugin).
+
+For more information, see [Working with quota on mobile browsers](http://www.html5rocks.com/en/tutorials/offline/quota-research/).
+
+{% include anchor.html class="h3" title="What data types does PouchDB support?" hash="data_types" %}
+
+PouchDB has two types of data: documents and attachments.
+
+#### Documents
+
+As in CouchDB, the documents you store must be serializable as JSON. Modifying the `Object` prototype or storing classes is not supported.
+
+IndexedDB will actually support non-JSON data (e.g. `Date`s aren't stringified), but you should not rely on this, because CouchDB, LevelDB, and Web SQL do not behave the same.
+
+#### Attachments
+
+PouchDB also supports attachments, which are the most efficient way to store binary data. Attachments may either be supplied as base64-encoded strings or as `Blob` objects.
+
+Different backends have different strategies for storing binary data, which may affect the overall database size. Attachment stubs have a `length` property that describes the number of bytes in the `Blob` object, but under the hood, it may actually take up more space than that.
+
+PouchDB's strategies are:
+
+* **Blob**: data is stored in a true binary format. The most efficient method.
+* **UTF-16 Blob**: blobs are coerced to UTF-16, so they takes up 2x the normal space.
+* **Base-64**: data is stored as a base-64-encoded string. The least efficient method.
+
+Here are the strategies used by various browsers in PouchDB:
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Adapter
+
IE (10+)
+
Firefox
+
Chrome < 43, Android
+
Chrome >= 43
+
Safari < 7.1, iOS < 8
+
Safari < 10.1, >= 7.1, iOS < 10.3, >= 8
+
Safari >= 10.1, iOS >= 10.3
+
+
+
IndexedDB
+
Blob
+
Blob
+
Base-64
+
Blob
+
+
+
Blob
+
+
+
WebSQL
+
+
+
Blob
+
Blob
+
UTF-16 Blob
+
Blob
+
+
+
+
+
+Attachments are deduplicated based on their MD5 sum, so duplicate attachments won't take up extra space.
+
+To truly remove an attachment from the data store, you will need to use [compaction]({{ site.baseurl }}/api.html#compaction) to remove document revisions that reference that attachment.
+
+{% include anchor.html class="h3" title="Is it safe to upgrade PouchDB?" hash="safe_to_upgrade" %}
+
+Since v1.0.0, PouchDB has supported automatic schema migrations. This means that if you open a database that was built with an older version of PouchDB, the newer version will run all the steps necessary to get the old database up to date. We have extensive tests in place to guarantee that this feature works correctly.
+
+Even in the case of a major version release, PouchDB still performs the schema migrations. So for instance, you can create a database with PouchDB 1.0.0 and it will still work after you open it in 3.0.0.
+
+Once a database is migrated, however, you can no longer open it with an older version of PouchDB. So if an update contains a migration, it will be clearly marked in the release notes.
+
+{% include anchor.html class="h3" title="How is PouchDB different from CouchDB?" hash="couchdb_differences" %}
+
+PouchDB is also a CouchDB client, and you should be able to switch between a local database or an online CouchDB instance without changing any of your application's code.
+
+However, there are some minor differences to note:
+
+**View Collation** - CouchDB uses ICU to order keys in a view query; in PouchDB they are ASCII ordered.
+
+**View Offset** - CouchDB returns an `offset` property in the view results. In PouchDB, `offset` just mirrors the `skip` parameter rather than returning a true offset.
diff --git a/docs/version/latest/getting-started.md b/docs/version/latest/getting-started.md
new file mode 100644
index 0000000000..ea42b4df88
--- /dev/null
+++ b/docs/version/latest/getting-started.md
@@ -0,0 +1,215 @@
+---
+layout: 2ColLeft.html
+title: Getting Started Guide
+sidebar: ./nav.html
+---
+
+{% include alert/start.html variant="info" %}
+
+If you get stuck, download a working version,
+or check out the full repo
+and commit history.
+
+{% include alert/end.html %}
+
+In this tutorial we will write a basic Todo web application based on [TodoMVC](http://todomvc.com/) that syncs to an online CouchDB server. It should take around 10 minutes.
+
+{% include anchor.html class="h3" title="Video Tutorial" hash="video_tutorial" %}
+
+Prefer video tutorials? This guide is available in video format:
+
+{% include iframe.html src="https://www.youtube.com/embed/-Z7UF2TuSp0" %}
+
+{% include anchor.html class="h3" title="Download Assets" hash="download" %}
+
+We will start with a template of the project where all the data related functions have been replaced with empty stubs. Download and unzip [pouchdb-getting-started-todo.zip](https://github.com/pouchdb/pouchdb-getting-started-todo/archive/master.zip). When dealing with XHR and IndexedDB you are better off running web pages from a server as opposed to a filesystem. To do this you can run:
+
+{% highlight bash %}
+$ cd pouchdb-getting-started-todo
+$ python -m SimpleHTTPServer # for Python 2
+$ python -m http.server # for Python 3
+{% endhighlight %}
+
+Then visit [http://127.0.0.1:8000/](http://127.0.0.1:8000/). If you see the following screenshot, you are good to go:
+
+{% include img.html src="screenshots/todo-1.png" alt="Todo Screenshot" %}
+
+It's also a good idea to open your browser's console so you can see any errors or confirmation messages.
+
+{% include anchor.html class="h3" title="Installing PouchDB" hash="installing_pouchdb" %}
+
+Open `index.html` and include PouchDB in the app by adding a script tag:
+
+{% highlight html %}
+
+
+
+{% endhighlight %}
+
+PouchDB is now installed in your app and ready to use! (In production, you should use a local copy of the script.)
+
+{% include anchor.html class="h3" title="Creating a database" hash="creating_a_database" %}
+
+The rest of the work will be done inside `app.js`. We will start by creating a database to enter your todos. To create a database simply instantiate a new PouchDB object with the name of the database:
+
+{% highlight js %}
+// EDITING STARTS HERE (you don't need to edit anything above this line)
+
+const db = new PouchDB('todos');
+const remoteCouch = false;
+{% endhighlight %}
+
+You don't need to create a schema for the database. After giving it a name, you can immediately start writing objects to it.
+
+{% include anchor.html class="h3" title="Write todos to the database" hash="write_todos_to_database" %}
+
+The first thing we shall do is start writing items to the database. The main input will call `addTodo` with the current text when the user presses `Enter`. We can complete this function with the following code:
+
+{% highlight js %}
+function addTodo(text) {
+ const todo = {
+ _id: new Date().toISOString(),
+ title: text,
+ completed: false
+ };
+ db.put(todo, function callback(err, result) {
+ if (!err) {
+ console.log('Successfully posted a todo!');
+ }
+ });
+}
+{% endhighlight %}
+
+In PouchDB each document is required to have a unique `_id`. Any subsequent writes to a document with the same `_id` will be considered updates. Here we are using a date string as an `_id`. For our use case, it will be unique, and it can also be used to sort items in the database. You can use `db.post()` if you want random ids. The `_id` is the only thing required when creating a new document. The rest of the object you can create as you like.
+
+The `callback` function will be called once the document has been written (or failed to write). If the `err` argument is not null, then it will have an object explaining the error, otherwise the `result` will hold the result.
+
+{% include anchor.html class="h3" title="Show items from the database" hash="show_database_items" %}
+
+We have included a helper function `redrawTodosUI` that takes an array of todos to display, so all we need to do is read the todos from the database. Here we will simply read all the documents using `db.allDocs`. The `include_docs` option tells PouchDB to give us the data within each document, and the `descending` option tells PouchDB how to order the results based on their `_id` field, giving us newest first.
+
+{% highlight js %}
+function showTodos() {
+ db.allDocs({include_docs: true, descending: true}, function(err, doc) {
+ redrawTodosUI(doc.rows);
+ });
+}
+{% endhighlight %}
+
+Once you have included this code, you should be able to refresh the page to see any todos you have entered.
+
+{% include anchor.html class="h3" title="Update the UI" hash="update_the_ui" %}
+
+We don't want to refresh the page to see new items. More typically you would update the UI manually when you write data to it, however, in PouchDB you may be syncing data remotely, so you want to make sure you update whenever the remote data changes. To do this we will call `db.changes` which subscribes to updates to the database, wherever they come from. You can enter this code between the `remoteCouch` and `addTodo` declaration:
+
+{% highlight js %}
+const remoteCouch = false;
+
+db.changes({
+ since: 'now',
+ live: true
+}).on('change', showTodos);
+
+// We have to create a new todo document and enter it in the database
+function addTodo(text) {
+{% endhighlight %}
+
+So every time an update happens to the database, we redraw the UI to show the new data. The `live` flag means this function will continue to run indefinitely. Now try entering a new todo and it should appear immediately.
+
+{% include anchor.html class="h3" title="Edit a todo" hash="edit_a_todo" %}
+
+When the user checks a checkbox, the `checkboxChanged` function will be called, so we'll fill in the code to edit the object and call `db.put`:
+
+{% highlight js %}
+function checkboxChanged(todo, event) {
+ todo.completed = event.target.checked;
+ db.put(todo);
+}
+{% endhighlight %}
+
+This is similar to creating a document, however the document must also contain a `_rev` field (in addition to `_id`), otherwise the write will be rejected. This ensures that you don't accidentally overwrite changes to a document.
+
+You can test that this works by checking a todo item and refreshing the page. It should stay checked.
+
+{% include anchor.html class="h3" title="Delete an object" hash="delete_an_object" %}
+
+To delete an object you can call db.remove with the object.
+
+{% highlight js %}
+function deleteButtonPressed(todo) {
+ db.remove(todo);
+}
+{% endhighlight %}
+
+Similar to editing a document, both the `_id` and `_rev` properties are required. You may notice that we are passing around the full object that we previously read from the database. You can of course manually construct the object, like: `{_id: todo._id, _rev: todo._rev}`, but passing around the existing object is usually more convenient and less error prone.
+
+{% include anchor.html class="h3" title="Complete rest of the Todo UI" hash="complete_todo_ui" %}
+
+`todoBlurred` is called when the user edits a document. Here we'll delete the document if the user has entered a blank title, and we'll update it otherwise.
+
+{% highlight js %}
+function todoBlurred(todo, event) {
+ const trimmedText = event.target.value.trim();
+ if (!trimmedText) {
+ db.remove(todo);
+ } else {
+ todo.title = trimmedText;
+ db.put(todo);
+ }
+}
+{% endhighlight %}
+
+{% include anchor.html class="h3" title="Installing CouchDB" hash="installing_couchdb" %}
+
+Now we'll implement the syncing. You need to have a compatible server instance. You can install either [PouchDB-Server](https://github.com/pouchdb/pouchdb-server), [CouchDB](http://couchdb.apache.org/) or use an hosted Couch service such as [Cloudant](https://cloudant.com/)
+
+{% include anchor.html class="h3" title="Enabling CORS" hash="enabling_cors" %}
+
+To replicate directly with CouchDB, you need to make sure CORS is enabled. Only set the username and password if you have set them previously. By default, CouchDB will be installed in "Admin Party," where username and password are not needed. You will need to replace `myname.example.com` with your own host (`127.0.0.1:5984` if installed locally):
+
+You can enable CORS in CouchDB using `curl` or the Futon web interface, but we've saved you some time by making a Node script called [add-cors-to-couchdb](https://github.com/pouchdb/add-cors-to-couchdb). Just run:
+
+{% highlight bash %}
+$ npm install -g add-cors-to-couchdb
+$ add-cors-to-couchdb
+{% endhighlight %}
+
+Or if your database is not at `127.0.0.1:5984`:
+
+{% highlight bash %}
+$ add-cors-to-couchdb http://me.example.com -u myusername -p mypassword
+{% endhighlight %}
+
+You can check that CORS is now enabled by visiting [http://localhost:5984/_utils/config.html](http://localhost:5984/_utils/config.html) in your browser. You should see something like this:
+
+{% include img.html src="cors_in_couchdb.png" alt="CORS settings in CouchDB" %}
+
+{% include anchor.html class="h3" title="Implement basic two way sync" hash="basic_two_way_sync" %}
+
+Now we will have the todo list sync. Back in `app.js` we need to specify the address of the remote database. Remember to replace `user`, `pass` and `myname.example.com` with the credentials of your own CouchDB instance:
+
+{% highlight js %}
+// EDITING STARTS HERE (you don't need to edit anything above this line)
+
+const db = new PouchDB('todos');
+const remoteCouch = 'http://user:pass@myname.example.com/todos';
+{% endhighlight %}
+
+Then we can implement the sync function like so:
+
+{% highlight js %}
+function sync() {
+ syncDom.setAttribute('data-sync-state', 'syncing');
+ const opts = {live: true};
+ db.replicate.to(remoteCouch, opts, syncError);
+ db.replicate.from(remoteCouch, opts, syncError);
+}
+{% endhighlight %}
+
+`db.replicate()` tells PouchDB to transfer all the documents `to` or `from` the `remoteCouch`. This can either be a string identifier or a PouchDB object. We call this twice: once to receive remote updates, and once to push local changes. Again, the `live` flag is used to tell PouchDB to carry on doing this indefinitely. The callback will be called whenever this finishes. For live replication, this will mean an error has occurred, like losing your connection or you canceled the replication.
+
+You should be able to open [the todo app](http://127.0.0.1:8000) in another browser and see that the two lists stay in sync with any changes you make to them. You may also want to look at your CouchDB's Futon administration page and see the populated database.
+
+{% include anchor.html class="h3" title="Congratulations!" hash="congratulations" %}
+
+You've completed your first PouchDB application. This is a basic example, and a real world application will need to integrate more error checking, user signup, etc. But you should now understand the basics you need to start working on your own PouchDB project. If you have any more questions, please get in touch on [IRC](ircs://irc.libera.chat:6697#pouchdb) [(web)](https://web.libera.chat/#pouchdb) or the [mailing list](https://groups.google.com/forum/#!forum/pouchdb).
diff --git a/docs/version/latest/guides/async-code.md b/docs/version/latest/guides/async-code.md
new file mode 100644
index 0000000000..9f087c88c2
--- /dev/null
+++ b/docs/version/latest/guides/async-code.md
@@ -0,0 +1,211 @@
+---
+index: 6
+layout: guide.html
+title: Asynchronous code
+sidebar: guides_nav.html
+---
+
+PouchDB provides a fully **asynchronous** API. This ensures that when you talk to PouchDB, the UI doesn't stutter, because the DOM is not being blocked by database operations.
+
+However, working with asynchronous code can be very complex, especially if you're only accustomed to synchronous APIs. So it's worth going over some of the basics.
+
+{% include anchor.html title="I promise to call you back..." hash="i-promise-to-call-you-back" %}
+
+To make things as flexible as possible for PouchDB users, the API is provided in both **callback** format and **promise** format.
+
+The **callback** format looks like this:
+
+```js
+db.get('mittens', function (error, doc) {
+ if (error) {
+ // oh noes! we got an error
+ } else {
+ // okay, doc contains our document
+ }
+});
+```
+
+The **promise** format looks like this:
+
+```js
+db.get('mittens').then(function (doc) {
+ // okay, doc contains our document
+}).catch(function (err) {
+ // oh noes! we got an error
+});
+```
+
+Basically, if you include a callback as the last argument in a function, then PouchDB assumes you want the callback style. Otherwise it assumes you want the promise style.
+
+{% include anchor.html title="Let's talk about promises" hash="lets-talk-about-promises" %}
+
+For this guide, we will use the **promise** format for a few reasons:
+
+1. Callbacks easily lead to spaghetti code, or to the [pyramid of doom](https://medium.com/@wavded/managing-node-js-callback-hell-1fe03ba8baf).
+2. Promises generally lead to better code organization, although they do have a steep learning curve.
+
+If you already understand promises, you can [skip to the next section](updating-deleting.html).
+
+{% include alert/start.html variant="info"%}
+{% markdown %}
+
+**What about async/await?** Async functions are an experimental ES7 syntax that enhances promise-based APIs by adding
+the `async` and `await` keywords. For more information about `async`/`await`, read [our introductory blog post]({{ site.baseurl }}/2015/03/05/taming-the-async-beast-with-es7.html).
+
+{% endmarkdown %}
+{% include alert/end.html%}
+
+{% include anchor.html title="Understanding promises" hash="understanding-promises" %}
+
+If you have the time, you are strongly encouraged to watch [this 50-minute video: "Redemption from Callback Hell"](http://youtu.be/hf1T_AONQJU). The rest of this chapter basically summarizes that video.
+
+The best way to think of promises is that they bring keywords like `return` and `try/catch` to asynchronous code.
+
+Synchronous code:
+
+```js
+function returnSomething() {
+ try {
+ doSomething();
+ doSomethingElse();
+ return true;
+ } catch (err) {
+ console.log(err);
+ }
+}
+```
+
+Asynchronous code:
+
+```js
+function returnSomething() {
+ return doSomething().then(function () {
+ return doSomethingElse();
+ }).then(function () {
+ return true;
+ }).catch(function (err) {
+ console.log(err);
+ });
+}
+```
+
+{% include anchor.html title="Use `catch()` to catch errors" hash="use-catch-to catch errors" %}
+
+The big advantage of working with Promises in asynchronous code is that you can always attach a `catch` function to the end of a big promise chain, and any errors that occur along the way will show up at the end.
+
+This avoids endless `if (err) {}` checking in the callback world:
+
+```js
+doSomething(function (err, result) {
+ if (err) {
+ // handle error
+ }
+ doSomethingElse(function (err, result) {
+ if (err) {
+ // handle error again...
+ }
+ doSomethingYetAgain(function (err, result) {
+ if (err) {
+ // seriously? okay, handle error again...
+ }
+ });
+ });
+});
+```
+
+Instead, in the promise world, you can have a long chain of asynchronous operations with a single `catch` at the end. To use PouchDB as an example:
+
+```js
+db.put({_id: 'charlie', age: 21}).then(function () {
+ return db.get('charlie');
+}).then(function (charlie) {
+ // increment Charlie's age
+ charlie.age++;
+ return db.put(charlie);
+}).then(function () {
+ return db.get('charlie');
+}).then(function (charlie) {
+ // increment Charlie's age again
+ charlie.age++;
+ return db.put(charlie);
+}).then(function () {
+ return db.get('charlie');
+}).then(function (charlie) {
+ console.log(charlie);
+}).catch(function (err) {
+ console.log(err);
+});
+```
+
+You should see:
+
+```js
+{"age":23,"_id":"charlie","_rev":"3-e794618b4e39ed566cc68b56f5426e8e"}
+```
+
+You can see **[a live example](http://bl.ocks.org/nolanlawson/612f95cbbb69eaafc2d5)** of this code.
+
+In this example, we put/get a document 3 times in a row. At the very end, there is a `catch()` statement to catch any errors along the way.
+
+What kind of errors might we run into? Well, let's imagine that we accidentally misspell the id `'charlie'` at some point. In this case, we will gracefully catch the error. Here's another **[live example](http://bl.ocks.org/nolanlawson/0f1c815cb5fe74cff5fc)**.
+
+You should see:
+
+```js
+{"status":404,"name":"not_found","message":"missing"}
+```
+
+This is really nice! No matter where the misspelling is, the error can be handled within a single function. That's much nicer than having to do `if (err){}` an endless number of times!
+
+{% include anchor.html title="An alternate way of catching errors" hash="an-alternate-way-of-catching-errors" %}
+
+If you've been doing promises for awhile, you might have seen this instead:
+
+```js
+db.get('charlie').then(function (charlie) {
+ // we got the charlie doc
+}, function (err) {
+ // we got an error
+})
+```
+
+This is equivalent to:
+
+```js
+db.get('charlie').then(function (charlie) {
+ // we got the charlie doc
+}).catch(function (err) {
+ // we got an error
+})
+```
+
+The `catch()` method is just syntactic sugar. You can use either format.
+
+{% include anchor.html title="Promises 101" hash="promises-101" %}
+
+The `then()` method takes a function. What can you do within this function? Three things:
+
+* Return another promise
+* Throw an error
+* Return a non-promise object (or `undefined`)
+
+Another way to think of it is this:
+
+```js
+db.get('charlie').then(function (charlie) {
+ // Within this function, you can do
+ // try/catch/return like you normally would,
+ // and it will be handled asynchronously!
+}).then(function (result) {
+ // If the previous function returned something
+ // (or returned undefined), it will show up here
+ // as "result".
+}).catch(function (err) {
+ // If the previous function threw an error,
+ // it will show up here as "err".
+});
+```
+
+{% include anchor.html title="Next" hash="next" %}
+
+Now that you have a grasp on promises, let's learn about updating and deleting documents.
diff --git a/docs/version/latest/guides/attachments.md b/docs/version/latest/guides/attachments.md
new file mode 100644
index 0000000000..37c0fc37c7
--- /dev/null
+++ b/docs/version/latest/guides/attachments.md
@@ -0,0 +1,314 @@
+---
+index: 9
+layout: guide.html
+title: Working with attachments
+sidebar: guides_nav.html
+---
+
+Attachments are where PouchDB can get really fun.
+
+The big difference between storage engines like WebSQL/IndexedDB and the older localStorage API is that you can stuff [a lot more data](https://web.dev/storage-for-the-web/) in it.
+
+PouchDB attachments allow you to use that to full advantage to store images, MP3s, zip files, or whatever you want.
+
+{% include anchor.html title="How attachments are stored" hash="how-attachments-are-stored" %}
+
+As their name implies, attachments are *attached* to documents. You can work with attachments either in base64-encoded format, or as a [Blob](https://developer.mozilla.org/en-US/docs/Web/API/Blob).
+
+For example, here is a very simple document with a plain text attachment, stored as base64.
+
+```js
+db.put({
+ _id: 'mydoc',
+ _attachments: {
+ 'myattachment.txt': {
+ content_type: 'text/plain',
+ data: 'aGVsbG8gd29ybGQ='
+ }
+ }
+});
+```
+
+Our document has the usual `_id` field, but it also has a special `_attachments` field that holds the attachments. Documents can have as many attachments as you want.
+
+{% include alert/start.html variant="info" %}
+
+When you create an attachment, you need to specify its content_type, otherwise known as the MIME type. Common MIME types include 'text/plain' for plain text, 'image/png' for PNG images, and 'image/jpeg' for JPG images.
+
+{% include alert/end.html %}
+
+As it turns out, `'aGVsbG8gd29ybGQ='` is just the string `'hello world'` encoded in base64. You can use the `atob()` and `btoa()` methods in your browser to verify.
+
+```js
+btoa('hello world') // "aGVsbG8gd29ybGQ="
+atob('aGVsbG8gd29ybGQ=') // "hello world"
+```
+
+Let's see what happens after we store this document. If you try to `get()` it normally, you may be surprised to see that the attachment data itself isn't returned:
+
+```js
+db.get('mydoc').then(function (doc) {
+ console.log(doc);
+});
+```
+
+The returned document will look like this:
+
+```js
+{
+ "_attachments": {
+ "myattachment.txt": {
+ "content_type": "text/plain",
+ "digest": "md5-XrY7u+Ae7tCTyyK7j1rNww==",
+ "stub": true
+ }
+ },
+ "_id": "mydoc",
+ "_rev": "1-e8a84187bb4e671f27ec11bdf7320aaa"
+}
+```
+
+You can see **[a live example](http://bl.ocks.org/nolanlawson/0a4b1267d3a5b5edd7b1)** of this code.
+
+By default, PouchDB will only give you an attachment **stub**, which contains a `digest`, i.e. the [md5sum](http://en.wikipedia.org/wiki/Md5sum) of the binary attachment.
+
+To get the full attachments when using `get()` or `allDocs()`, you need to specify `{attachments: true}`:
+
+```js
+db.get('mydoc', {attachments: true}).then(function (doc) {
+ console.log(doc);
+});
+```
+
+Then you'll get back the full attachment, base64-encoded:
+
+```js
+{
+ "_attachments": {
+ "myattachment.txt": {
+ "content_type": "text/plain",
+ "digest": "md5-XrY7u+Ae7tCTyyK7j1rNww==",
+ "data": "aGVsbG8gd29ybGQ="
+ }
+ },
+ "_id": "mydoc",
+ "_rev": "1-e8a84187bb4e671f27ec11bdf7320aaa"
+}
+```
+
+You can see **[a live example](http://bl.ocks.org/nolanlawson/b6d6164035f1fa0d38a8)** of this code.
+
+{% include anchor.html title="Image attachments" hash="image-attachments" %}
+
+Plaintext is cool and all, but you know what would be *really* awesome? Storing images.
+
+So let's do it! In this example, we'll put a document with a small icon attachment, represented as a base64-encoded string. Then we'll fetch it and display the icon as a normal `` tag:
+
+```js
+db.put({
+ _id: 'meowth',
+ _attachments: {
+ 'meowth.png': {
+ content_type: 'image/png',
+ data: 'iVBORw0KGgoAAAANSUhEUgAAACgAAAAkCAIAAAB0Xu9BAAAABGdBTUEAALGPC/xhBQAAAuNJREFUWEetmD1WHDEQhDdxRMYlnBFyBIccgdQhKVcgJeQMpE5JSTd2uqnvIGpVUqmm9TPrffD0eLMzUn+qVnXPwiFd/PP6eLh47v7EaazbmxsOxjhTT88z9hV7GoNF1cUCvN7TTPv/gf/+uQPm862MWTL6fff4HfDx4S79/oVAlAUwqOmYR0rnazuFnhfOy/ErMKkcBFOr1vOjUi2MFn4nuMil6OPh5eGANLhW3y6u3aH7ijEDCxgCvzFmimvc95TekZLyMSeJC68Bkw0kqUy1K87FlpGZqsGFCyqEtQNDdFUtFctTiuhnPKNysid/WFEFLE2O102XJdEE+8IgeuGsjeJyGHm/xHvQ3JtKVsGGp85g9rK6xMHtvHO9+WACYjk5vkVM6XQ6OZubCJvTfPicYPeHO2AKFl5NuF5UK1VDUbeLxh2BcRGKTQE3irHm3+vPj6cfCod50Eqv5QxtwBQUGhZhbrGVuRia1B4MNp6edwBxld2sl1splfHCwfsvCZfrCQyWmX10djjOlWJSSy3VQlS6LmfrgNvaieRWx1LZ6s9co+P0DLsy3OdLU3lWRclQsVcHJBcUQ0k9/WVVrmpRzYQzpgAdQcAXxZzUnFX3proannrYH+Vq6KkLi+UkarH09mC8YPr2RMWOlEqFkQClsykGEv7CqCUbXcG8+SaGvJ4a8d4y6epND+pEhxoN0vWUu5ntXlFb5/JT7JfJJqoTdy9u9qc7ax3xJRHqJLADWEl23cFWl4K9fvoaCJ2BHpmJ3s3z+O0U/DmzdMjB9alWZtg4e3yxzPa7lUR7nkvxLHO9+tvJX3mtSDpwX8GajB283I8R8a7D2MhUZr1iNWdny256yYLd52DwRYBtRMvE7rsmtxIUE+zLKQCDO4jlxB6CZ8M17GhuY+XTE8vNhQiIiSE82ZsGwk1pht4ZSpT0YVpon6EvevOXXH8JxVR78QzNuamupW/7UB7wO/+7sG5V4ekXb4cL5Lyv+4IAAAAASUVORK5CYII='
+ }
+ }
+}).then(function () {
+ return db.getAttachment('meowth', 'meowth.png');
+}).then(function (blob) {
+ const url = URL.createObjectURL(blob);
+ const img = document.createElement('img');
+ img.src = url;
+ document.body.appendChild(img);
+}).catch(function (err) {
+ console.log(err);
+});
+```
+
+You can see **[a live example](http://bl.ocks.org/nolanlawson/2a5f98a66c9fe3ae3532)** of this code.
+
+You should be unsurprised to see a cat smiling back at you. If the kitten theme bothers you, then you haven't been on the Internet very long.
+
+How does this code work? First off, we are making use of the `URL.createObjectURL()` method, which is a standard HTML5 method that converts a `Blob` to a URL that we can easily use as the `src` of an `img`.
+
+Second off, we are using the `getAttachment()` API, which returns a `Blob` rather than a base64-encoded string. To be clear: we can always convert between base64 and `Blob`s, but in this case, `getAttachment()` is just more convenient.
+
+
+{% include anchor.html title="Directly storing binary data" hash="directly-storing-binary-data" %}
+
+Up to now, we've been supplying our attachments as base64-encoded strings. But we can also create the Blobs ourselves and store those directly in PouchDB.
+
+Another shortcut we can use is the `putAttachment()` API, which simply modifies the existing document to hold a new attachment. Or, if the document does not exist, it will create an empty one.
+
+{% include alert/start.html variant="info" %}
+
+In Node.js, PouchDB uses Buffers instead of Blobs. Otherwise, the same rules apply.
+
+{% include alert/end.html %}
+
+For instance, we can read the image data from an `` tag using a `canvas` element, and then directly write that Blob to PouchDB:
+
+```js
+function convertImgToBlob(img, callback) {
+ const canvas = document.createElement('canvas');
+ const context = canvas.getContext('2d');
+ context.drawImage(img, 0, 0);
+
+ // Warning: toBlob() isn't supported by every browser.
+ // You may want to use blob-util.
+ canvas.toBlob(callback, 'image/png');
+}
+
+const catImage = document.getElementById('cat');
+convertImgToBlob(catImage, function (blob) {
+ db.putAttachment('meowth', 'meowth.png', blob, 'image/png').then(function () {
+ return db.get('meowth', {attachments: true});
+ }).then(function (doc) {
+ console.log(doc);
+ });
+});
+```
+
+You can see **[a live example](http://bl.ocks.org/nolanlawson/edaf09b84185418a55d9)** of this code.
+
+This stores exactly the same image content as in the other example, which you can confirm by checking the base64-encoded output.
+
+{% include alert/start.html variant="warning" %}
+
+Blobs can be tricky to work with, especially when it comes to cross-browser support.
+You may find blob-util to be a useful
+addition to the attachment API. For instance, it has an
+imgSrcToBlob() method that will work cross-browser.
+
+{% include alert/end.html %}
+
+{% include anchor.html title="Allow the user to store an attachment" hash="allow-the-user-to-store-an-attachment" %}
+
+You can also upload a file with the HTML5 `File` API and store it directly in the database, because the data you get from the `` element is already a `Blob`.
+(See: [Blob API](https://developer.mozilla.org/en-US/docs/Web/API/Blob) and [File API](https://developer.mozilla.org/en-US/docs/Web/API/File), which inherits properties from the `Blob` Interface.)
+
+Here is an example of allowing a user to choose a file from their filesystem:
+
+```html
+
+```
+
+And then "uploading" that file directly into PouchDB:
+
+```js
+const input = document.querySelector('input');
+input.addEventListener('change', function () {
+ const file = input.files[0]; // file is a Blob
+
+ db.put({
+ _id: 'mydoc',
+ _attachments: {
+ filename: {
+ content_type: file.type,
+ data: file
+ }
+ }
+ }).catch(function (err) {
+ console.log(err);
+ });
+});
+```
+
+You can see **[a live example](http://bl.ocks.org/ntwcklng/f57b03c15e91c25e2cb5)** of this code.
+
+Select a file and you will see the stored file, `size`, and `type`, which are valid `Blob` properties. If you choose an image, it will also show the image!
+
+{% include anchor.html title="Base64 vs Blobs/Buffers" hash="base64-vs-blobs-buffers" %}
+
+Whether you supply attachments as base64-encoded strings or as Blobs/Buffers, PouchDB will try to store them in [the most efficient way](/faq.html#data_types).
+
+So when you insert your attachments, either format is acceptable. For instance, you can put Blobs/Buffers using `put()`:
+
+```js
+db.put({
+ _id: 'mydoc',
+ _attachments: {
+ 'myattachment.txt': {
+ content_type: 'text/plain',
+ data: myBlob
+ }
+ }
+});
+```
+
+And you can also pass base64-encoded strings to `putAttachment()`:
+
+```js
+db.putAttachment('mydoc', 'myattachment.png', myBase64String, 'image/png');
+```
+
+You can also insert multiple attachments at once using `put()`:
+
+```js
+db.put({
+ _id: 'mydoc',
+ _attachments: {
+ 'myattachment1.txt': {
+ content_type: 'text/plain',
+ data: myBlob1
+ },
+ 'myattachment2.txt': {
+ content_type: 'text/plain',
+ data: myBlob2
+ },
+ 'myattachment3.txt': {
+ content_type: 'text/plain',
+ data: myBlob3
+ },
+ // etc.
+ }
+});
+```
+
+The `bulkDocs()` and `post()` APIs also accept attachments in either format.
+
+When you fetch attachments, however, `getAttachment()` will always return Blobs/Buffers.
+
+The other "read" APIs, such as `get()`, `allDocs()`, `changes()`, and `query()` have an `{attachments: true}` option that returns the attachments base64-encoded strings. If you add `{binary: true}`, though, they will return Blobs/Buffers.
+
+{% include alert/start.html variant="info" %}
+{% markdown %}
+
+**Performance tip:** If you can insert and retrieve your attachments using _only_ Blobs/Buffers, then you will typically get better performance, especially when it comes to memory usage. The base64 string format is mostly provided for developer convenience and debugging.
+
+{% endmarkdown %}
+{% include alert/end.html %}
+
+
+{% include anchor.html title="Blob types" hash="blob-types" %}
+
+Blobs have their own `type`, but there is also a `content_type` that you specify when you store it in PouchDB:
+
+```js
+const myBlob = new Blob(['I am plain text!'], {type: 'text/plain'});
+console.log(myBlob.type); // 'text/plain'
+
+db.put({
+ _id: 'mydoc',
+ _attachments: {
+ 'myattachment.txt': {
+ content_type: 'text/plain',
+ data: myBlob
+ }
+ }
+});
+```
+
+The reason for this redundancy is 1) Buffers in Node do not have a `type`, and 2) the CouchDB attachment format requires it.
+
+So for best results, you should ensure that your Blobs have the same type as the one reported to PouchDB. Otherwise you may see inconsistent behavior (e.g. in IndexedDB, where the Blob is stored as-is on compatible browsers).
+
+{% include anchor.html title="Related API documentation" hash="related-api-documentation" %}
+
+* [putAttachment()](/api.html#save_attachment)
+* [getAttachment()](/api.html#get_attachment)
+* [removeAttachment()](/api.html#delete_attachment)
+
+{% include anchor.html title="Next" hash="next" %}
+
+Now that you can attach cat pictures to all your documents (and why wouldn't you?), let's talk about replication.
diff --git a/docs/version/latest/guides/bulk-operations.md b/docs/version/latest/guides/bulk-operations.md
new file mode 100644
index 0000000000..28ae8634fe
--- /dev/null
+++ b/docs/version/latest/guides/bulk-operations.md
@@ -0,0 +1,132 @@
+---
+index: 8
+layout: guide.html
+title: Bulk operations
+sidebar: guides_nav.html
+---
+
+You can `get()`, `put()`, and `remove()` single documents to your heart's content, but a database isn't a database unless it can handle many operations at once!
+
+PouchDB provides two methods for bulk operations - `bulkDocs()` for bulk writes, and `allDocs()` for bulk reads.
+
+{% include anchor.html title="Use `bulkDocs()` to write many docs" hash="use-bulkdocs-to-write-many-docs" %}
+
+The `bulkDocs()` API is very simple. It just takes a list of documents that you want to `put()` into the database:
+
+```js
+db.bulkDocs([
+ {
+ _id: 'mittens',
+ occupation: 'kitten',
+ cuteness: 9.0
+ },
+ {
+ _id: 'katie',
+ occupation: 'kitten',
+ cuteness: 7.0
+ },
+ {
+ _id: 'felix',
+ occupation: 'kitten',
+ cuteness: 8.0
+ }
+]);
+```
+
+You can see **[a live example](http://bl.ocks.org/nolanlawson/038a45134341f3b7235b)** of this code.
+
+This code is equivalent to `put()`ing each document separately:
+
+```js
+db.put({
+ _id: 'mittens',
+ occupation: 'kitten',
+ cuteness: 9.0
+}).then(function () {
+ return db.put({
+ _id: 'katie',
+ occupation: 'kitten',
+ cuteness: 7.0
+ });
+}).then(function () {
+ return db.put({
+ _id: 'felix',
+ occupation: 'kitten',
+ cuteness: 8.0
+ });
+});
+```
+
+{% include anchor.html title="Why bulk up with `bulkDocs()`?" hash="why-bulk-up-with-bulkdocs" %}
+
+Bulk operations tend to be faster than individual operations, because they can be combined into a single transaction (in a local IndexedDB/WebSQL) or a single HTTP request (in a remote CouchDB).
+
+You can also update or delete multiple documents this way. You just need to include the `_rev` and `_deleted` values as previously discussed. The same rules as for `put()` apply to each individual document.
+
+{% include alert/start.html variant="warning" %}
+
+Neither bulkDocs() nor allDocs() constitutes a transaction in the traditional sense. That means that, if a single put() fails, you should not assume that the others will fail.
+
+
+By design, CouchDB and PouchDB do not support transactions. A document is the smallest unit of operations.
+{% include alert/end.html %}
+
+{% include anchor.html title="Use `allDocs()` to read many docs" hash="use-alldocs-to-read-many-docs" %}
+
+Likewise, `allDocs()` is a method that allows you to read many documents at once.
+
+Most crucially, when you read from `allDocs()`, the documents are returned *sorted by order of `_id`*. This makes the `_id` a very powerful field that you can use for more than just uniquely identifying your documents.
+
+For instance, if you refer back to [the live example](http://bl.ocks.org/nolanlawson/038a45134341f3b7235b) above, you'll notice that the kittens are sorted by their name, because their names are used as their `_id`s.
+
+Another common way to take advantage of this is to use `new Date().toJSON()` as your document `_id`s. In this way, all your documents will be sorted by date.
+
+For instance, let's save three kittens with three different dates, and then fetch them sorted by date:
+
+```js
+db.put({
+ _id: new Date().toJSON(),
+ name: 'Mittens',
+ occupation: 'kitten',
+ cuteness: 9.0
+}).then(function () {
+ return db.put({
+ _id: new Date().toJSON(),
+ name: 'Katie',
+ occupation: 'kitten',
+ cuteness: 7.0
+ });
+}).then(function () {
+ return db.put({
+ _id: new Date().toJSON(),
+ name: 'Felix',
+ occupation: 'kitten',
+ cuteness: 8.0
+ });
+}).then(function () {
+ return db.allDocs({include_docs: true});
+}).then(function (response) {
+ console.log(response);
+}).catch(function (err) {
+ console.log(err);
+});
+```
+
+You can see **[a live example](http://bl.ocks.org/nolanlawson/8f58dbc360348a4c95f6)** to confirm that the kittens are sorted by the order they were put into the database.
+
+{% include anchor.html title="Please use `allDocs()`. Seriously." hash="please-use-alldocs" %}
+
+`allDocs()` is the unsung star of the PouchDB world. It not only returns documents in order – it also allows you to reverse the order, filter by `_id`, slice and dice using "greater than" and "less than" operations on the `_id`, and much more.
+
+Far too many developers overlook this valuable API, because they misunderstand it. When a developer says "my PouchDB app is slow!", it is usually because they are using the slow `query()` API when they should be using the fast `allDocs()` API.
+
+For details on how to effectively use `allDocs()`, you are strongly recommended to read ["Pagination strategies with PouchDB"]({{ site.baseurl }}/2014/04/14/pagination-strategies-with-pouchdb.html). For 99% of your applications, you should be able to use `allDocs()` for all the pagination/sorting/searching functionality that you need.
+
+{% include anchor.html title="Related API documentation" hash="related-api-documentation" %}
+
+* [bulkDocs()](/api.html#batch_create)
+* [allDocs()](/api.html#batch_fetch)
+
+{% include anchor.html title="Next" hash="next" %}
+
+Now that you've fallen helplessly in love with `bulkDocs()` and `allDocs()`, let's turn our wandering gaze to attachments.
diff --git a/docs/version/latest/guides/changes.md b/docs/version/latest/guides/changes.md
new file mode 100644
index 0000000000..0bc3168016
--- /dev/null
+++ b/docs/version/latest/guides/changes.md
@@ -0,0 +1,142 @@
+---
+index: 12
+layout: guide.html
+title: Changes feed
+sidebar: guides_nav.html
+---
+
+One of the brilliant things about CouchDB replication is that it makes it easy to learn about changes made to the database over time. CouchDB allows you to easily answer questions like:
+
+* What changes occurred to the database since a given time?
+* What changes occurred to this document?
+* What did this database look like a few days ago?
+
+For all of these and related questions, there's the `changes()` API.
+
+{% include anchor.html title="Basic changes usage" hash="basic-changes-usage" %}
+
+If you want to simply fetch all changes since the beginning of time, you can do:
+
+```js
+db.changes({
+ since: 0,
+ include_docs: true
+}).then(function (changes) {
+
+}).catch(function (err) {
+ // handle errors
+});
+```
+
+You can see a **[live example](http://bl.ocks.org/nolanlawson/7c32861af5d31a8fac4a)** of this code.
+
+Then you will have a list of all changes made to the database, in the order that they were made.
+
+One thing you will notice about the changes feed is that it actually omits non-leaf revisions to documents. For instance, in the live example, we skip from `seq` 1 immediately to `seq` 3.
+
+This is by design – the changes feed only tells us about leaf revisions. However, the order of those leaf revisions is determined by the order they were put in the database. So you may notice that `'firstDoc'` still appears before `'secondDoc'`, which appears before `'thirdDoc'`.
+
+Also notice the option `{include_docs: true}`. By default, the documents themselves are not included in the changes feed; only the `id`s, `rev`s, and whether or not they were `deleted`. With `{include_docs: true}`, however, each non-deleted change will have a `doc` property containing the new or modified document.
+
+{% include anchor.html title="Changes pagination" hash="changes-pagination" %}
+
+If you expect this to be a very large number of changes, you can also use the `limit` option to do pagination:
+
+```js
+const pageSize = 10;
+let lastSeq = 0;
+function fetchNextPage() {
+ return db.changes({
+ since: lastSeq,
+ limit: pageSize
+ }).then(function (changes) {
+ if (changes.results.length < pageSize) {
+ // done!
+ } else {
+ lastSeq = changes.last_seq;
+ return fetchNextPage();
+ }
+ });
+}
+
+fetchNextPage().catch(function (err) {
+ // handle errors
+});
+```
+
+You can see a **[live example](http://bl.ocks.org/nolanlawson/dcdeae555b31c2a6d332)** of this code.
+
+{% include anchor.html title="`seq` versus `_rev`" hash="seq-versus-rev" %}
+
+The changes feed lists each change with a corresponding `seq` integer. `seq` always starts with 0, and beyond that it increases monotonically. (In Cloudant, these are strings rather than integers.)
+
+`seq` can be thought of as a version number for the entire database. Basically it answers the question of "How many total changes have been made to all documents in this database?" This sets it apart from the revision hash `_rev`, which marks the changes made to a single document.
+
+However, the `seq` between two databases is not guaranteed to be kept in sync. CouchDB and PouchDB have slightly different ways of increasing their `seq` values, so really `seq` is only meaningful within a single database.
+
+{% include anchor.html title="Live changes feed" hash="live-changes-feed" %}
+
+Just like replication, you can also listen to a live changes feed. The way this works is very similar to the replication API:
+
+```js
+db.changes({
+ since: 'now'
+}).on('change', function (change) {
+ // received a change
+}).on('error', function (err) {
+ // handle errors
+});
+```
+
+In the above example, we've also taken advantage of the quasi-magical `'now'` option for `since`, which will give us all changes from the moment we start listening.
+
+This can be very useful for scenarios where you want to update the UI whenever something in the database changes, such as for a real-time chat application.
+
+{% include anchor.html title="Understanding changes" hash="understanding-changes" %}
+
+There are two types of changes:
+
+* Added or modified documents
+* Deleted documents
+
+To distinguish between the two types in a live `changes()` listener,
+you can use the following code:
+
+```js
+db.changes({
+ since: 'now',
+ live: true,
+ include_docs: true
+}).on('change', function (change) {
+ // change.id contains the doc id, change.doc contains the doc
+ if (change.deleted) {
+ // document was deleted
+ } else {
+ // document was added/modified
+ }
+}).on('error', function (err) {
+ // handle errors
+});
+```
+
+You can see a [live example](http://bl.ocks.org/nolanlawson/fa42662cdfeeaa7b78fc) of this code.
+
+Notice that `change.doc` contains the document (unless it's deleted), because we used `{include_docs: true}`.
+
+Also notice that new documents always have revisions starting with the string `'1-'`. Subsequent revisions start with `'2-'`, `'3-'`, `'4-'`, etc.
+
+{% include alert/start.html variant="info" %}
+
+
How can I distinguish between added and modified documents? Checking if the revision starts with '1-' is a pretty good trick. However, this will not work for databases that are replication targets, because replication only sends the latest versions of documents. This means that the '1-' revision may get skipped entirely, and the local database will only receive the 2nd, 3rd or 4th (etc.) revision. Conflicting revisions will also appear in the changes feed.
+
+
So the short answer is that you cannot. If you are trying to mirror changes in a non-Pouch structure (e.g. a list of DOM elements), then the best solution is to search all the DOM elements to see if the document already exists, or to re-run allDocs() for every change.
+
+{% include alert/end.html %}
+
+{% include anchor.html title="Related API documentation" hash="related-api-documentation" %}
+
+* [changes()](/api.html#changes)
+
+{% include anchor.html title="Next" hash="next" %}
+
+Now that we know how to hook our data spigot to the changes feed, let's look into using the very powerful `find()` API.
diff --git a/docs/version/latest/guides/compact-and-destroy.md b/docs/version/latest/guides/compact-and-destroy.md
new file mode 100644
index 0000000000..2c9ed1a9d2
--- /dev/null
+++ b/docs/version/latest/guides/compact-and-destroy.md
@@ -0,0 +1,97 @@
+---
+index: 15
+layout: guide.html
+title: Compacting and destroying
+sidebar: guides_nav.html
+---
+
+By default, PouchDB and CouchDB are designed to store all document revisions forever. This is very similar to how Git works, and it helps ensure that two databases can consistently replicate with each other.
+
+However, if you allow your database to grow without bounds, it can end up taking up much more space than you need. This can especially be a problem in [browsers with storage quotas](/faq.html#data_limits).
+
+To mitigate this problem, PouchDB offers two recourses: compaction and destruction.
+
+{% include anchor.html title="Compacting a database" hash="compacting-a-database" %}
+
+When you compact a database, you tell PouchDB to optimize its current storage usage. CouchDB will do the same thing:
+
+```js
+return db.compact().then(function (info) {
+ // compaction complete
+}).catch(function (err) {
+ // handle errors
+});
+```
+
+From the API perspective, nothing should be different about the database after compaction, *except* that non-leaf revisions will no longer be available.
+
+```js
+db.put({_id: 'foo', version: 1}).then(function () {
+ return db.get('foo');
+}).then(function (doc) {
+ doc.version = 2;
+ return db.put(doc);
+}).then(function () { )
+ return db.compact();
+}).then(function () {
+ // DANGER!
+ // From now on, revision 1 is no longer available.
+}).catch(function (err) {
+ // handle errors
+});
+```
+
+You can see a **[live example](http://bl.ocks.org/nolanlawson/ff6eb521793e3a199864)** of this code.
+
+Compaction is a great feature, but it may not be what you desire if you want to retain a document's history from the beginning of time.
+
+However, if that's not a concern, then compaction is a harmless operation. In fact, since leaf revisions are retained, this means that you can still do [conflict resolution](/guides/conflicts.html) after compaction!
+
+{% include anchor.html title="Auto-compaction" hash="auto-compaction" %}
+
+If you really want to go all-in on compaction, then you can even put your database in `auto_compaction` mode. This means that it will automatically perform a `compact()` operation after every write.
+
+```js
+const db = new PouchDB('mydb', {auto_compaction: true});
+db.put({_id: 'foo', version: 1}).then(function () {
+ return db.get('foo');
+}).then(function (doc) {
+ doc.version = 2;
+ return db.put(doc);
+}).then(function () {
+ // Revision 1 is already unavailable!
+}).catch(function (err) {
+ // handle errors
+});
+```
+
+You can see a **[live example](http://bl.ocks.org/nolanlawson/b88f46d7cbaef8d93cba)** of this code.
+
+This feature is only available in local databases, not remote ones. On remote databases, the `auto_compaction` option will do nothing.
+
+{% include anchor.html title="Destroying a database" hash="destroying-a–database" %}
+
+We all love our databases, but sometimes good things must come to an end, and you need to snub out a database completely.
+
+So if you want to give your database to a nice farm family upstate, then the `destroy()` API is for you. It's very simple:
+
+```js
+new PouchDB('mydb').destroy().then(function () {
+ // database destroyed
+}).catch(function (err) {
+ // error occurred
+})
+```
+
+Note that destroying a database does not mean that replicated databases will also be destroyed. Destruction has nothing to do with the normal `put()`/`remove()` operations on documents, so it has no impact on replication.
+
+Also note that in Web SQL, the database will not really be destroyed – it will just have its tables dropped. This is because Web SQL does not support true database deletion.
+
+{% include anchor.html title="Related API documentation" hash="related-api-documentation" %}
+
+* [compact()](/api.html#compaction)
+* [destroy()](/api.html#delete_database)
+
+{% include anchor.html title="Next" hash="next" %}
+
+To wrap up, let's look at a special class of documents in PouchDB – local docs.
diff --git a/docs/version/latest/guides/conflicts.md b/docs/version/latest/guides/conflicts.md
new file mode 100644
index 0000000000..60898fe779
--- /dev/null
+++ b/docs/version/latest/guides/conflicts.md
@@ -0,0 +1,160 @@
+---
+index: 11
+layout: guide.html
+title: Conflicts
+sidebar: guides_nav.html
+---
+
+Conflicts are an unavoidable reality when dealing with distributed systems. And make no mistake: client-server *is* a distributed system.
+
+CouchDB and PouchDB differ from many other sync solutions, because they bring the issue of conflicts front-and-center. With PouchDB, conflict resolution is entirely under your control.
+
+{% include alert/start.html variant="info" %}
+
+PouchDB exactly implements CouchDB's replication algorithm, so conflict resolution works the same in both. For the purposes of this article, "CouchDB" and "PouchDB" may be used interchangeably.
+
+{% include alert/end.html %}
+
+{% include anchor.html title="Two types of conflicts" hash="two-types-of-conflicts" %}
+
+In CouchDB, conflicts can occur in two places: immediately, when you try to commit a new revision, or later, when two peers have committed changes to the same document. Let's call these **immediate conflicts** and **eventual conflicts**.
+
+### Immediate conflicts
+
+**Immediate conflicts** can occur with any API that takes a `rev` or a document with `_rev` as input – `put()`, `post()`, `remove()`, `bulkDocs()`, and `putAttachment()`. They manifest as a `409` (conflict) error:
+
+```js
+const myDoc = {
+ _id: 'someid',
+ _rev: '1-somerev'
+};
+db.put(myDoc).then(function () {
+ // success
+}).catch(function (err) {
+ if (err.name === 'conflict') {
+ // conflict!
+ } else {
+ // some other error
+ }
+});
+```
+
+In your code, *you should always be handling conflicts*. No matter how unlikely it may seem, 409s can and do occur.
+
+For instance, if you are doing live replication, a document may be modified by somebody else while the user is working on it. If the remote changes are replicated to the local database before the user tries to commit their changes, then they will receive the above 409 error.
+
+#### Upsert
+
+In many cases, the most practical solution to the 409 problem is to retry the `put()` until it succeeds. If the user's intended change can be expressed as a **delta** (i.e. a change that doesn't depend on the current revision), then this is very easy to achieve.
+
+Borrowing a phrase from traditional databases, let's call this an **upsert** ("update or insert"), and use the [pouchdb-upsert](https://github.com/pouchdb/pouchdb-upsert) plugin to implement it:
+
+```js
+function myDeltaFunction(doc) {
+ doc.counter = doc.counter || 0;
+ doc.counter++;
+ return doc;
+}
+
+db.upsert('my_id', myDeltaFunction).then(function () {
+ // success!
+}).catch(function (err) {
+ // error (not a 404 or 409)
+});
+```
+
+This `upsert()` function takes a `docId` and `deltaFunction`, where the `deltaFunction` is just a function that takes a document and outputs a new document. (If the document does not exist, then an empty document is provided.)
+
+`pouchdb-upsert` also offers a `putIfNotExists()` function, which will create a document if it doesn't exist already. For more details, see [the plugin's documentation](https://github.com/pouchdb/pouchdb-upsert#readme).
+
+### Eventual conflicts
+
+Now, let's move on to the second type: **eventual conflicts**.
+
+Imagine two PouchDB databases have both gone offline. The two separate users each make modifications to the same document, and then they come back online at a later time.
+
+Both users committed changes to the same version of the document, and their local databases did not throw 409 errors. What happens then?
+
+This is the classic "conflict" scenario, and CouchDB handles it very elegantly. By default, CouchDB will choose an arbitrary winner based on a deterministic algorithm, which means both users will see the same winner once they're back online. However, since the replication history is stored, you can always go back in time to resolve the conflict.
+
+To detect if a document is in conflict, you use the `{conflicts: true}` option when you `get()` it.
+
+```js
+db.get('docid', {conflicts: true}).then(function (doc) {
+ // do something with the doc
+}).catch(function (err) {
+ // handle any errors
+});
+```
+
+If the document has conflicts, then the `doc` will be returned with a `_conflicts` attribute, which may contain the revision IDs of conflicting revisions.
+
+For instance, imagine the `doc` returned is the following:
+
+```js
+{
+ "_id": "docid",
+ "_rev": "2-x",
+ "_conflicts": ["2-y"]
+}
+```
+
+Here we have two separate revisions (`2-x` and `2-y`) written by two separate databases, and one database's revision (`2-x`) has arbitrarily won.
+
+{% include alert/start.html variant="warning" %}
+{% markdown %}
+
+Normally, `_rev`s look more like `2-c1592ce7b31cc26e91d2f2029c57e621`, i.e. a digit followed by a very long hash. In these examples, `x` and `y` are used in place of the hash, for simplicity's sake.
+
+{% endmarkdown %}
+{% include alert/end.html %}
+
+Notice that the document's current revision starts with `2-`, and the conflicting version also starts with `2-`, indicating that they're both at the same level of the revision tree. (Revision hashes start with `1-`, `2-`, `3-`, etc., which indicates their distance from the first, "root" revision. The root always starts with `1-`.)
+
+Both databases will see the same conflict, assuming replication has completed. In fact, all databases in the network will see the exact same revision history – much like Git.
+
+To fetch the losing revision, you simply `get()` it using the `rev` option:
+
+```js
+db.get('docid', {rev: '2-y'}).then(function (doc) {
+ // do something with the doc
+}).catch(function (err) {
+ // handle any errors
+});
+```
+
+At this point, you can present both versions to the user, or resolve the conflict automatically using your preferred conflict resolution strategy: last write wins, first write wins, [RCS](https://www.gnu.org/software/rcs/), etc.
+
+To mark a conflict as resolved, all you need to do is `remove()` the unwanted revisions. So for instance, to remove `'2-y'`, you would do:
+
+```js
+db.remove('docid', '2-y').then(function (doc) {
+ // yay, we're done
+}).catch(function (err) {
+ // handle any errors
+});
+```
+
+If you want to resolve the conflict by creating a new revision, you simply `put()` a new document on top of the current winner, and make sure that the losing revision is deleted.
+
+{% include anchor.html title="Accountants don't use erasers" hash="accountants-dont-use-erasers" %}
+
+Another conflict resolution strategy is to design your database so that conflicts are impossible. In practice, this means that you never update or remove existing documents – you only create new documents.
+
+This strategy has been called the "every doc is a delta" strategy. A classic use-case for this would be a checkbook app, where every document is simply an operation that increases or decreases the account balance:
+
+```js
+{_id: new Date().toJSON(), change: 100} // balance increased by $100
+{_id: new Date().toJSON(), change: -50} // balance decreased by $50
+{_id: new Date().toJSON(), change: 200} // balance increased by $200
+```
+
+In this system, it is impossible for two documents to conflict, because the document `_id`s are just timestamps. Ledger transactions are recorded in the order they were made, and at the end of the day, you only need to do an `allDocs()` or `query()` operation to sum the result.
+
+The wisdom of this strategy can be expressed by the maxim: ["Accountants don't use erasers"](https://queue.acm.org/detail.cfm?id=2884038). Like a diligent accountant, your app can just add new documents when you want to make a change, rather than going back and scrubbing out previous changes.
+
+There is also a PouchDB plugin that implements this strategy: [delta-pouch](https://github.com/redgeoff/delta-pouch).
+
+{% include anchor.html title="Next" hash="next" %}
+
+Now that we've settled our conflicts, let's take a look at the changes feed.
diff --git a/docs/version/latest/guides/databases.md b/docs/version/latest/guides/databases.md
new file mode 100644
index 0000000000..6e1052799c
--- /dev/null
+++ b/docs/version/latest/guides/databases.md
@@ -0,0 +1,146 @@
+---
+index: 4
+layout: guide.html
+title: Working with databases
+sidebar: guides_nav.html
+---
+
+PouchDB databases come in two flavors: local and remote.
+
+{% include anchor.html title="Local databases" hash="local–databases" %}
+
+To create a local database, you simply call `new PouchDB` and give it a name:
+
+```js
+const db = new PouchDB('kittens');
+```
+
+You can see a **[live example](http://bl.ocks.org/nolanlawson/bddac54b92c2d8d39241)** of this code.
+
+{% include alert/start.html variant="info" %}
+
+Protip: whenever you see a live example in this guide, you can download it to follow along at home! For example, to run this example, just enter the following commands in your command prompt:
+
+
+ git clone https://gist.github.com/bddac54b92c2d8d39241.git kittens
+ cd kittens
+ python -m SimpleHTTPServer # for Python 2
+ python -m http.server # for Python 3
+
+
+Now the site is up and running at http://localhost:8000. To find the correct gist.github.com URL, just click the "block" number at the top of the page.
+
+{% include alert/end.html %}
+
+{% include anchor.html title="Remote databases" hash="remote-databases" %}
+
+To create a remote database, you call `new PouchDB` and give it a path to a database in CouchDB.
+
+```js
+const db = new PouchDB('http://localhost:5984/kittens');
+```
+
+{% include alert/start.html variant="info" %}
+note: The remote database will not be created until you do an API call, e.g.: db.info(). The reason behind that is that the PouchDB constructor is completely
+synchronous, for ease of error handling (i.e. no asynchronous errors).
+{% include alert/end.html %}
+
+The structure of a CouchDB URL is very simple:
+
+```
+http:// localhost:5984 /kittens
+⌞_____⌟ ⌞____________⌟ ⌞_____⌟
+ | | |
+Protocol Where CouchDB database
+(https if itself is name
+Cloudant) hosted
+
+```
+
+If the remote database doesn't exist, then PouchDB will create it for you.
+
+You can verify that your database is working by visiting the URL [http://localhost:5984/kittens](http://localhost:5984/kittens). You should see something like this:
+
+```js
+{"db_name":"kittens","doc_count":0,"doc_del_count":0,"update_seq":0,"purge_seq":0,"compact_running":false,"disk_size":79,"data_size":0,"instance_start_time":"1410722558431975","disk_format_version":6,"committed_update_seq":0}
+```
+
+If instead you see:
+
+```js
+{"error":"not_found","reason":"no_db_file"}
+```
+
+Then check to make sure that your remote CouchDB has started up correctly. Common errors (such as CORS) are [listed here](/errors.html).
+
+
+{% include anchor.html title="Get basic info about the database" hash="get-basic-info-about-the–database" %}
+
+You can see basic information about the database by using the `info()` method.
+
+```js
+db.info().then(function (info) {
+ console.log(info);
+})
+```
+
+The local database should show something like:
+
+```js
+{"doc_count":0,"update_seq":0,"db_name":"kittens"}
+```
+
+The remote database may have a bit more information:
+
+```js
+{"db_name":"kittens","doc_count":0,"doc_del_count":0,"update_seq":0,"purge_seq":0,"compact_running":false,"disk_size":79,"data_size":0,"instance_start_time":"1410722558431975","disk_format_version":6,"committed_update_seq":0}
+```
+
+The most important bits of information are:
+
+* `doc_count`: the number of undeleted documents in the database
+* `db_name`: the name of the database
+
+
+{% include anchor.html title="Debugging" hash="debugging" %}
+
+### IndexedDB/WebSQL inspectors
+
+You can use the normal developer tools to see what your database looks like under the hood.
+
+In Chrome, just choose *Overflow icon* ☰ → *Tools* → *Developer Tools*. Then click the *Resources* tab, then *IndexedDB*, and you should see the following:
+
+{% include img.html src="dev_tools.png" alt="Chrome Developer Tools" %}
+
+This is the raw IndexedDB representation of your PouchDB, so it is very fine-grained. However, you may find it useful.
+
+In Safari, your database will be under *Develop* → *Show Web Inspector* → *Resources* → *Databases*.
+
+{% include img.html src="safari_inspector.png" alt="Web Inspector in Safari" %}
+
+{% include anchor.html title="Deleting your local database" hash="deleting-your-local-database" %}
+
+During development, it's often useful to destroy the local database, so you can see what your users will experience when they visit your site for the first time. A page refresh is not enough, because the data will still be there!
+
+In Chrome, you can use the [Clear Cache extension](https://chrome.google.com/webstore/detail/clear-cache/cppjkneekbjaeellbfkmgnhonkkjfpdn), which will add a trashcan icon to your toolbar, which you can click to delete all local data (IndexedDB, WebSQL, LocalStorage, cookies, etc.).
+
+In Firefox, you can use the [Clear Browsing Data add-on](https://addons.mozilla.org/en-US/firefox/addon/clear-browsing-data/), which adds a toolbar button to delete your IndexedDB with one click.
+
+In Safari, you can simply click *Safari* → *Clear History and Website Data*.
+
+{% include anchor.html title="Differences between the local and remote databases" hash="differences-between-the-local-and-remote-databases" %}
+
+When you create a local PouchDB database, it uses whatever underlying datastore is available - IndexedDB in most browsers, WebSQL in older browsers, and LevelDB in Node.js.
+
+When you create a remote PouchDB database, it communicates directly with the remote database – CouchDB, Cloudant, Couchbase, etc.
+
+The goal of PouchDB is to allow you to seamlessly communicate with one or the other. You should not notice many differences between the two, except that of course the local one is much faster!
+
+{% include anchor.html title="Related API documentation" hash="related-api-documentation" %}
+
+* [new PouchDB() (constructor)](/api.html#create_database)
+* [Debug mode](/api.html#debug_mode)
+
+{% include anchor.html title="Next" hash="next" %}
+
+Now that you've created some databases, let's put some documents in 'em!
diff --git a/docs/version/latest/guides/documents.md b/docs/version/latest/guides/documents.md
new file mode 100644
index 0000000000..4b3676e775
--- /dev/null
+++ b/docs/version/latest/guides/documents.md
@@ -0,0 +1,191 @@
+---
+index: 5
+layout: guide.html
+title: Working with documents
+sidebar: guides_nav.html
+---
+
+{% include anchor.html title="What's a document?" hash="whats-a–document" %}
+
+PouchDB is a NoSQL database, meaning that you store unstructured *documents* rather than explicitly specifying a schema with rows, tables, and all that jazz.
+
+A document might look like this:
+
+```js
+{
+ "_id": "mittens",
+ "name": "Mittens",
+ "occupation": "kitten",
+ "age": 3,
+ "hobbies": [
+ "playing with balls of yarn",
+ "chasing laser pointers",
+ "lookin' hella cute"
+ ]
+}
+```
+
+If you come from a SQL background, this handy conversion chart may help:
+
+
+
+
+
SQL concept
+
PouchDB concept
+
+
+
table
+
no equivalent
+
+
+
row
+
document
+
+
+
+
column
+
field
+
+
+
primary key
+
primary key (_id)
+
+
+
index
+
view
+
+
+
+
+We'll discuss these concepts later on.
+
+{% include anchor.html title="Storing a document" hash="storing-a–document" %}
+
+To store a document, you simply `put` it:
+
+```js
+const doc = {
+ "_id": "mittens",
+ "name": "Mittens",
+ "occupation": "kitten",
+ "age": 3,
+ "hobbies": [
+ "playing with balls of yarn",
+ "chasing laser pointers",
+ "lookin' hella cute"
+ ]
+};
+db.put(doc);
+```
+
+Whenever you `put()` a document, it must have an `_id` field so that you can retrieve it later.
+
+So now let's `get()` the document by using its `_id`:
+
+```js
+db.get('mittens').then(function (doc) {
+ console.log(doc);
+});
+```
+
+You should see:
+
+```js
+{
+ "name": "Mittens",
+ "occupation": "kitten",
+ "age": 3,
+ "hobbies": [
+ "playing with balls of yarn",
+ "chasing laser pointers",
+ "lookin' hella cute"
+ ],
+ "_id": "mittens",
+ "_rev": "1-bea5fa18e06522d12026f4aee6b15ee4"
+}
+```
+
+You can see a **[live example](http://bl.ocks.org/nolanlawson/c02bba75247012afb1bf)** of this code.
+
+The document looks exactly the same as when we put it, except... aha! What is this? There is a new field, `_rev`, that contains what looks like garbage. PouchDB gots some 'splainin' to do.
+
+{% include anchor.html title="Understanding revisions (`_rev`)" hash="understanding-revisions-rev" %}
+
+The new field, `_rev` is the *revision marker*. It is a randomly-generated ID that changes whenever a document is created or updated.
+
+Unlike most other databases, whenever you update a document in PouchDB or CouchDB, you must present the *entire document* along with its current *revision marker*.
+
+For instance, to increment Mittens' age to 4, we would do:
+
+```js
+doc.age = 4;
+doc._rev = "1-bea5fa18e06522d12026f4aee6b15ee4";
+db.put(doc);
+```
+
+If you fail to include the correct `_rev`, you will get the following sad error:
+
+```js
+{
+ "status": 409,
+ "name": "conflict",
+ "message": "Document update conflict"
+}
+```
+
+`HTTP 409` is a standard HTTP error message that indicates a conflict.
+
+{% include anchor.html title="Updating documents correctly" hash="updating-documents–correctly" %}
+
+So to update Mittens' age, we will first need to fetch Mittens from the database, to ensure that we have the correct `_rev` before we put them back. We don't need to manually assign the `_rev` value here (like we did above), as it is already in the `doc` we're fetching.
+
+```js
+// fetch mittens
+db.get('mittens').then(function (doc) {
+ // update their age
+ doc.age = 4;
+ // put them back
+ return db.put(doc);
+}).then(function () {
+ // fetch mittens again
+ return db.get('mittens');
+}).then(function (doc) {
+ console.log(doc);
+});
+```
+
+You can see a **[live example](http://bl.ocks.org/nolanlawson/d6daa02ca3875d1222dd)** of this code.
+
+{% include alert/start.html variant="info" %}
+
+Don't worry if the structure of this code seems strange! It's using promises, which will be discussed in the next chapter.
+
+{% include alert/end.html %}
+
+Now you should see the following:
+
+```js
+{
+ "name": "Mittens",
+ "occupation": "kitten",
+ "age": 4,
+ "hobbies": [
+ "playing with balls of yarn",
+ "chasing laser pointers",
+ "lookin' hella cute"
+ ],
+ "_id": "mittens",
+ "_rev": "2-3e3fd988b331193beeeea2d4221b57e7"
+}
+```
+
+As you can see, we have successfully updated Mittens' age to 4 (they grow up so fast!), and their revision marker has also changed to `"2-3e3fd988b331193beeeea2d4221b57e7"`. If we wanted to increment their age to 5, we would need to supply this new revision marker.
+
+{% include anchor.html title="Related API documentation" hash="related-api-documentation" %}
+
+* [get()](/api.html#fetch_document)
+* [put()](/api.html#create_document)
+
+{% include anchor.html title="Next" hash="next" %}
+
+Now that you understand a bit about how to create and update documents, let's take a small detour to talk about asynchronous code.
diff --git a/docs/version/latest/guides/guides.json b/docs/version/latest/guides/guides.json
new file mode 100644
index 0000000000..cd20b85a28
--- /dev/null
+++ b/docs/version/latest/guides/guides.json
@@ -0,0 +1,3 @@
+{
+ "tags": "guides"
+}
diff --git a/docs/version/latest/guides/index.md b/docs/version/latest/guides/index.md
new file mode 100644
index 0000000000..b22939bcdb
--- /dev/null
+++ b/docs/version/latest/guides/index.md
@@ -0,0 +1,56 @@
+---
+index: 1
+layout: guide.html
+nav: Intro
+title: Introduction to PouchDB
+sidebar: guides_nav.html
+---
+
+Welcome to the PouchDB guide! Consider this your starting point for anything and everything related to the world of PouchDB and CouchDB.
+
+For a quicker TodoMVC-based tutorial, you can also check out the ["Getting Started" guide]({{ site.baseurl }}/getting-started.html).
+
+Feel free to skip ahead using the sidebar at any time.
+
+{% include anchor.html title="What is PouchDB?" hash="what-is-pouchdb" %}
+
+**PouchDB** is a JavaScript implementation of [CouchDB](https://couchdb.apache.org). Its goal is to emulate the CouchDB API with near-perfect fidelity, while running in the browser or in Node.js.
+
+{% include anchor.html title="What is CouchDB?" hash="what-is-couchdb" %}
+
+**CouchDB** is a NoSQL database created in 2005 by Damien Katz, and now maintained by the Apache Software Foundation. If you are a JavaScript developer, you probably use CouchDB every day, because it's the core technology that powers [npm](https://www.npmjs.org/).
+
+{% include anchor.html title="Couchbase, CouchDB, Couch-what?" hash="couchbase-couchdb-couch-what" %}
+
+Today there are two major database companies that
+can trace their lineage back to CouchDB: [**Couchbase**](http://couchbase.com) and [**Cloudant**](http://cloudant.com). Both of them are separate products compared to CouchDB.
+
+However, all three of these databases share the same **CouchDB sync protocol**. This means that PouchDB can sync with either one of them, and you can always swap out one database for another. You're never locked in.
+
+In a sense, these databases are like competing phone companies, and the CouchDB sync protocol is the underlying telephony infrastructure.
+
+{% include anchor.html title="CouchDB's one-two punch: HTTP and sync" hash="http-and-sync" %}
+
+With so many SQL and NoSQL databases out there – MongoDB, PostgreSQL, MySQL, etc. – you may wonder why we chose to implement CouchDB instead of the others.
+
+We have two very good answers to that question: **HTTP** and **sync**.
+
+{% include anchor.html title="HTTP: the little protocol that could" hash="http" %}
+
+When working with databases, we're often accustomed to writing some kind of conversion layer between the database and our client-side applications. This means, however, that we are just translating database queries into RESTful HTTP calls, over and over. For every app we write.
+
+CouchDB throws this out the window by daring us to talk to the database directly, from our client-side apps. And it does so by using HTTP as its primary means of communication. No special protocol, no special drivers: just REST and HTTP. You can communicate with CouchDB entirely through your browser, `curl`, or a REST client like [Postman](https://chrome.google.com/webstore/detail/postman-rest-client/fdmmgilgnpjigdojojpjoooidkmcomcm).
+
+In this way, CouchDB truly is a "database for the web."
+
+{% include anchor.html title="Sync: CouchDB's killer feature" hash="sync" %}
+
+Another unique feature of CouchDB is that it was designed from the bottom-up to enable easy synchronization between different databases.
+
+For example, if you are worried about latency in your client-side applications, you can simply set up one CouchDB in Europe, another in North America, and another in Asia. After enabling continuous two-way replication between these databases, your clients can simply talk to whichever one is closer.
+
+PouchDB takes this one step further by putting the database inside your browser.
+
+{% include anchor.html title="Next" hash="next" %}
+
+Now that you understand the basics of the PouchDB/CouchDB universe, let's set up CouchDB!
diff --git a/docs/version/latest/guides/local-documents.md b/docs/version/latest/guides/local-documents.md
new file mode 100644
index 0000000000..e54a6c9158
--- /dev/null
+++ b/docs/version/latest/guides/local-documents.md
@@ -0,0 +1,44 @@
+---
+index: 16
+layout: guide.html
+title: Local documents
+sidebar: guides_nav.html
+---
+
+"Local" documents are a special class of documents in PouchDB and CouchDB, which are used for storing local metadata about a database. You might never need them in your own app, but sometimes they can come in handy for advanced use cases.
+
+{% include anchor.html title="Local docs in a nutshell" hash="local-docs-in-a-nutshell" %}
+
+Local docs have the following characteristics:
+
+* They don't replicate.
+* They can't contain attachments.
+* They don't appear in `allDocs()`, `changes()`, or `query()`.
+* However, you can modify them with `put()`/`remove()`/`bulkDocs()`, and you can fetch them with `get()`.
+
+So basically, local docs only exist *for that database*, and they don't mix with the "normal" documents.
+
+To create a local doc, you simply use `'_local/'` as the prefix of the `_id`. This is supported in both CouchDB and PouchDB:
+
+```js
+db.put({
+ _id: '_local/foobar',
+ someText: 'yo, this is my local doc!'
+}).then(function () {
+ return db.get('_local/foobar');
+});
+```
+
+{% include anchor.html title="Advantages of local docs" hash="advantages-of-local–docs" %}
+
+Local docs are useful for small bits of configuration or metadata, which you don't necessarily want to replicate, but which you want to keep in the database anyway. Many PouchDB plugins and core components use local docs. For instance, the replication algorithm uses them to store checkpoints, and map/reduce uses them to keep track of what's been `emit`ted.
+
+Local docs also have some good performance characteristics compared to regular docs. They don't have a version history, so only the most recent revision is ever stored in the database. This means that `put()`s and `get()`s are faster for local docs than for regular docs, and that local docs tend to take up less space on disk. In a sense, they are auto-compacted, although they take up even less space on disk than documents in a compacted database.
+
+Regardless, you need to provide the current `_rev` when you update local docs, just like with regular docs.
+
+{% include anchor.html title="Related API documentation" hash="related-api-documentation" %}
+
+* [put()](/api.html#create_document)
+* [get()](/api.html#fetch_document)
+* [remove()](/api.html#delete_document)
diff --git a/docs/version/latest/guides/mango-queries.md b/docs/version/latest/guides/mango-queries.md
new file mode 100644
index 0000000000..64ad8752e4
--- /dev/null
+++ b/docs/version/latest/guides/mango-queries.md
@@ -0,0 +1,364 @@
+---
+index: 13
+layout: guide.html
+title: Mango queries
+sidebar: guides_nav.html
+---
+
+Mango queries, also known as `pouchdb-find` or the `find()` API, are a structured query API that allows you to build _secondary indexes_ beyond the built-in `allDocs()` and `changes()` indexes.
+
+This API is useful for answering questions like:
+
+- find all documents where the `type` is `'user'`
+- find all users whose `age` is greater than `21`
+- find all Pokémon whose `name` starts with `'pika'`
+- etc.
+
+{% include anchor.html title="Installation" hash="installation" %}
+
+The `find()` API is currently offered as a separate plugin, meaning that you must install it on top of `pouchdb.js`. Here's how to do so:
+
+### Script tags
+
+```html
+
+
+```
+
+The `pouchdb.find.js` file is available in the `pouchdb` package in npm/Bower, on [unpkg](https://unpkg.com/pouchdb/dist/), or [as a GitHub download](https://github.com/apache/pouchdb/releases/download/{{ site.version }}/pouchdb.find.js). Note it must be placed after `pouchdb.js`.
+
+### npm
+
+If you are using Node, Browserify, Webpack, Rollup, etc., then you can install it like so:
+
+```bash
+npm install --save pouchdb-find
+```
+
+Then in code:
+
+```js
+const PouchDB = require('pouchdb');
+PouchDB.plugin(require('pouchdb-find'));
+```
+
+{% include anchor.html title="Query language" hash="query-language" %}
+
+The [Mango query language](https://github.com/cloudant/mango) is a DSL inspired by MongoDB, which allows you to define an index that is then used for querying. One quick way to understand how this works is to use the [live query demo](https://nolanlawson.github.io/pouchdb-find/).
+
+At a basic level, there are two steps to running a query: `createIndex()` (to define which fields to index) and `find()` (to query the index).
+
+For instance, let's imagine a simple index to look up all documents whose `name` is `"mario"`. First we'll create it:
+
+```js
+db.createIndex({
+ index: {fields: ['name']}
+});
+```
+
+This returns a Promise that resolves once the index is created. At this point, we have an index based on the `"name"` field, so we can use it for lookup:
+
+```js
+db.find({
+ selector: {
+ name: 'mario'
+ }
+});
+```
+
+This returns a Promise containing an array of all documents that match this selector. Note that this is equivalent to using the `$eq` (equals) operator:
+
+```js
+db.find({
+ selector: {
+ name: {$eq: 'mario'}
+ }
+});
+```
+
+The important thing to understand is that, for a typical database, `createIndex()` is the expensive operation, because it is looping through all documents in the database and building a [B-tree](https://en.wikipedia.org/wiki/B-tree) based on the `name` value.
+
+Once the B-tree is built up, though, the `find()` is relatively cheap. (If this were _not_ the case, then we would be better off just using `allDocs()` to iterate through the database ourselves!)
+
+Once we have an index on `name`, we can also sort all documents by `name`:
+
+```js
+db.find({
+ selector: {
+ name: {$gte: null}
+ },
+ sort: ['name']
+});
+```
+
+Note that we are specifying that the `name` must be greater than or equal to `null`, which is a workaround for the fact that the Mango query language requires us to have a selector. In [CouchDB collation order](https://docs.couchdb.org/en/stable/ddocs/views/collation.html), `null` is the "lowest" value, and so this will return all documents regardless of their `name` value.
+
+{% include anchor.html title="Pagination" hash="pagination" %}
+
+Reading all documents in the database and sorting them by a particular value is neat, but we could do this ourselves with `allDocs()`, and it would have the same performance impact. Where it gets more interesting is when we use `limit`:
+
+```js
+db.find({
+ selector: {
+ name: {$gte: null}
+ },
+ sort: ['name'],
+ limit: 10
+});
+```
+
+In this case, we only get 10 documents back, but they are the first 10 documents, sorted by name. This means that we have only read 10 documents out of the database into memory, which can be used for [efficient pagination]({{ site.baseurl }}/2014/04/14/pagination-strategies-with-pouchdb.html).
+
+For instance, if we are displaying the first 10 results on a single page, and the user clicks "next" to see the next page, we can restructure our query based on the last result, to continue the pagination. Let's imagine the first 10 documents' `name`s are:
+
+```js
+[
+ 'abby', 'bertrand', 'clarice', 'don', 'emily',
+ 'fumiko', 'gunther', 'horatio', 'ike', 'joy'
+]
+```
+
+For our next 10 pages of results, the query becomes:
+
+```js
+db.find({
+ selector: {
+ name: {$gt: 'joy'}
+ },
+ sort: ['name'],
+ limit: 10
+});
+```
+
+Because we are now specifying that the `name` must be greater than `'joy'`, we are guaranteed to get the next-highest result after `'joy'`, which may (for instance) look like this:
+
+```js
+[
+ 'kim', 'lin', 'maria', 'nell', 'oliver',
+ 'pat', 'quincy', 'roy', 'sam', 'tanya'
+]
+```
+
+In this way, we can continue paginating by using the last value as our next starting point. At any given point in time, there are only 10 documents stored in memory at once, which is great for performance.
+
+{% include anchor.html title="Indexing on more than one field" hash="more-than-one-field" %}
+
+Sometimes an index is not as simple as "find all documents whose `name` is `"mario"`. Sometimes you want to do something fancy, such as "find all documents whose `name` is `"mario"` and whose `age` is greater than `21`". In those cases, you can index on more than one field:
+
+```js
+db.createIndex({
+ index: {
+ fields: ['name', 'age']
+ }
+}).then(function () {
+ return db.find({
+ selector: {
+ name: 'mario',
+ age: {$gt: 21}
+ }
+ });
+});
+```
+
+One thing to note is that the order of these fields matters when creating your index. For instance, the following would _not_ work:
+
+```js
+/* THIS WON'T WORK! */
+db.createIndex({
+ index: {
+ fields: ['age', 'name']
+ }
+}).then(function () {
+ return db.find({
+ selector: {
+ name: 'mario',
+ age: {$gt: 21}
+ }
+ });
+});
+```
+
+The reason for this is easy to understand if we imagine how this index would sort a hypothetical database:
+
+
+
+
+
name
+
age
+
+
+
Luigi
+
17
+
+
+
Luigi
+
28
+
+
+
Mario
+
18
+
+
+
Mario
+
21
+
+
+
Mario
+
22
+
+
+
Mario
+
26
+
+
+
Peach
+
17
+
+
+
Peach
+
21
+
+
+
Peach
+
25
+
+
+
+
+In the above table, the documents are sorted by `['name', 'age']`, and our "Marios above the age of 21" are very clearly grouped together.
+
+However, if we were to change the order, and sort them by `['age', 'name']`, it would look instead like this:
+
+
+
+
+
age
+
name
+
+
+
17
+
Luigi
+
+
+
17
+
Peach
+
+
+
18
+
Mario
+
+
+
21
+
Mario
+
+
+
21
+
Peach
+
+
+
22
+
Mario
+
+
+
25
+
Peach
+
+
+
26
+
Mario
+
+
+
28
+
Luigi
+
+
+
+
+If we imagine our `find()` query as a "slice" of the data, it's obvious that there's no slice that corresponds to "all Marios whose age is greater than 21." Instead, our documents are sorted by `age`, and then documents with the same `age` are sorted by `name`.
+
+This index may be good for answering questions like "find all 17-year-olds whose name starts with letters N-Z", but it's not very good for answering questions like "find all people with a certain name, older than a certain age."
+
+This shows that it's important to carefully design an index before creating a query to use that index. Otherwise, the query planner may fall back to in-memory querying, which can be expensive.
+
+{% include anchor.html title="Performance notes" hash="performance-notes" %}
+
+The Mango query language is generally very permissive, and allows you to write queries that may not perform very well, but will run regardless. For instance, you may create an index with `createIndex()`, but then write a `find()` query that doesn't actually use that index. In general, the query planner tries to find the most appropriate index, but it may fall back to in-memory querying.
+
+As a straightforward example, if you query using the `_id` field, then the query planner will automatically map that directly to an `allDocs()` query. However, if you query for a field that isn't yet indexed, then it will simply use `allDocs()` to read in all documents from the database (!) and then filter in-memory. This can lead to poor performance, especially if your database is large.
+
+If you're ever wondering how the query planner is interpreting your query, you can use the explain endpoint:
+
+```js
+db.explain({
+ selector: {
+ name: 'mario',
+ age: {$gt: 21}
+ }
+})
+.then(function (explained) {
+ // detailed explained info can be viewed
+});
+
+```
+
+In the console, the query planner will show a detailed explanation of how it has interpreted the query, whether it uses any indexes, and whether any parts of the query need to be executed in-memory.
+
+
+You may also want to pay attention to the `"warning"` value included in your results set, indicating that there was no index that matched the given query. For instance, the warning may look like this:
+
+```js
+{
+ "docs": [ /* ... */ ],
+ "warning": "No matching index found, create an index to optimize query time."
+}
+```
+
+{% include anchor.html title="Set which index to use" hash="use_index" %}
+
+When creating a query, by settings the `use_index` field, it is possible to tell pouchdb-find which index to use.
+The below example shows how to do that.
+
+```js
+db.createIndex({
+ index: {
+ fields: ['age', 'name'],
+ ddoc: "my-index-design-doc"
+ }
+}).then(function () {
+ return db.find({
+ selector: {
+ name: 'mario',
+ age: {$gt: 21},
+ },
+ use_index: 'my-index-design-doc'
+ });
+});
+```
+
+{% include anchor.html title="Further reading" hash="further-reading" %}
+
+The Mango query language is quite large and supports many options. Some of the more common ones include:
+
+* `$eq`: equals
+* `$gt`: greater than
+* `$gte`: greater than or equal to
+* `$lt`: less than
+* `$lte`: less than or equal to
+
+There are many more options besides these, although note that not all of them can take advantage of indexes. For instance, `$regex`, `$ne`, and `$not` cannot use on-disk indexes, and must use in-memory filtering instead.
+
+The most complete documentation for selector options can be found in the [CouchDB `_find` documentation](https://docs.couchdb.org/en/stable/api/database/find.html). You might also look at the [Cloudant Query Language](https://docs.cloudant.com/cloudant_query.html) documentation (which is nearly identical to Mango, other than `text` and other Cloudant-specific features). PouchDB uses CouchDB as the reference implementation; they ought to be functionally identical.
+
+It should be noted that, over HTTP, this API currently works with CouchDB 2.0+, Cloudant, and PouchDB Server.
+CouchDB 2.0 is the reference implementation, so the API should be the same. CouchDB 1.6.1 and below is not supported.
+
+{% include anchor.html title="Related API documentation" hash="related-api-documentation" %}
+
+* [createIndex()](/api.html#create_index)
+* [find()](/api.html#query_index)
+* [getIndexes()](/api.html#list_indexes)
+* [deleteIndex()](/api.html#delete_index)
+
+{% include anchor.html title="Next" hash="next" %}
+
+Now that we've learned how to do structured Mango queries, let's try some more advanced queries, using _map/reduce_.
diff --git a/docs/version/latest/guides/queries.md b/docs/version/latest/guides/queries.md
new file mode 100644
index 0000000000..46bcfb409a
--- /dev/null
+++ b/docs/version/latest/guides/queries.md
@@ -0,0 +1,240 @@
+---
+index: 14
+layout: guide.html
+title: Map/reduce queries
+sidebar: guides_nav.html
+---
+
+Map/reduce queries, also known as the `query()` API, are one of the most powerful features in PouchDB. However, they can be quite tricky to use, and so this guide is designed to dispell some of the mysteries around them.
+
+The first thing to understand is that you don't need map/reduce queries if you merely want to look up documents by `_id` or sort them by `_id`. The `allDocs()` API already does this, using an efficient built-in index (see ["bulk operations"](bulk-operations.html) for details).
+
+The second thing to know is that map/reduce is also unnecessary if you want to sort documents by their update time – this is exactly what the [changes feed](changes.html) does! Again, this is a built-in index that you get for free.
+
+Finally, it's important to understand that [Mango queries](mango-queries.html) are much easier to use than map/reduce queries, and they can usually satisfy 99% of use cases. The point of map/reduce is to provide an _extremely advanced_ API for building secondary indexes, suitable for those with specific querying needs.
+
+So now that you've read the fine print, let's talk about how map/reduce queries actually work!
+
+{% include anchor.html title="Mappin' and reducin'" hash="mappin-and-reducin" %}
+
+The PouchDB `query()` API (which corresponds to the `_view` API in CouchDB) has two modes: temporary queries and persistent queries.
+
+### Temporary queries
+
+**Temporary queries** are very slow, and we only recommend them for quick debugging during development. To use a temporary query, you simply pass in a `map` function:
+
+```js
+db.query(function (doc, emit) {
+ emit(doc.name);
+}, {key: 'foo'}).then(function (result) {
+ // found docs with name === 'foo'
+}).catch(function (err) {
+ // handle any errors
+});
+```
+
+In the above example, the `result` object will contain stubs of documents where the `name` attribute is equal to `'foo'`. To include the document in each row of results, use the `include_docs` option.
+
+{% include alert/start.html variant="info" %}
+
+The emit pattern is part of the standard CouchDB map/reduce API. What the function basically says is, "for each document, emit doc.name as a key."
+
+{% include alert/end.html %}
+
+### Persistent queries
+
+**Persistent queries** are much faster, and are the intended way to use the `query()` API in your production apps. To use persistent queries, there are two steps.
+
+First, you create a **design document**, which describes the `map` function you would like to use:
+
+```js
+// document that tells PouchDB/CouchDB
+// to build up an index on doc.name
+const ddoc = {
+ _id: '_design/my_index',
+ views: {
+ by_name: {
+ map: function (doc) { emit(doc.name); }.toString()
+ }
+ }
+};
+// save it
+pouch.put(ddoc).then(function () {
+ // success!
+}).catch(function (err) {
+ // some error (maybe a 409, because it already exists?)
+});
+```
+
+{% include alert/start.html variant="info" %}
+
+The .toString() at the end of the map function is necessary to prep the
+object for becoming valid JSON.
+
+{% include alert/end.html %}
+
+{% include alert/start.html variant="info" %}
+
+The emit function will be available in scope when the map function is
+run, so don't pass it in as a parameter.
+
+{% include alert/end.html %}
+
+Then you actually query it, by using the name you gave the design document when you saved it:
+
+```js
+db.query('my_index/by_name').then(function (res) {
+ // got the query results
+}).catch(function (err) {
+ // some error
+});
+```
+
+Note that, the first time you query, it will be quite slow because the index isn't
+built until you query it. To get around this, you can do an empty query to kick
+off a new build:
+
+```js
+db.query('my_index/by_name', {
+ limit: 0 // don't return any results
+}).then(function (res) {
+ // index was built!
+}).catch(function (err) {
+ // some error
+});
+```
+
+After this, your queries will be much faster.
+
+{% include alert/start.html variant="info"%}
+
+CouchDB builds indexes in exactly the same way as PouchDB. So you may want to familiarize yourself with the "stale" option in order to get the best possible performance for your app.
+
+{% include alert/end.html %}
+
+
+{% include anchor.html title="More about map/reduce" hash="more-about-map-reduce" %}
+
+That was a fairly whirlwind tour of the `query()` API, so let's get into more detail about how to write your map/reduce functions.
+
+#### Indexes in SQL databases
+
+Quick refresher on how indexes work: in relational databases like MySQL and PostgreSQL, you can usually query whatever field you want:
+
+```sql
+SELECT * FROM pokemon WHERE name = 'Pikachu';
+```
+
+But if you don't want your performance to be terrible, you first add an index:
+
+```sql
+ALTER TABLE pokemon ADD INDEX myIndex ON (name);
+```
+
+The job of the index is to ensure the field is stored in a B-tree within the database, so your queries run in _O(log(n))_ time instead of _O(n)_ time.
+
+#### Indexes in NoSQL databases
+
+All of the above is also true in document stores like CouchDB and MongoDB, but conceptually it's a little different. By default, documents are assumed to be schemaless blobs with one primary key (called `_id` in both Mongo and Couch), and any other keys need to be specified separately. The concepts are largely the same; it's mostly just the vocabulary that's different.
+
+In CouchDB, queries are called _map/reduce functions_. This is because, like most NoSQL databases, CouchDB is designed to scale well across multiple computers, and to perform efficient query operations in parallel. Basically, the idea is that you divide your query into a _map_ function and a _reduce_ function, each of which may be executed in parallel in a multi-node cluster.
+
+#### Map functions
+
+It may sound daunting at first, but in the simplest (and most common) case, you only need the _map_ function. A basic map function might look like this:
+
+```js
+function myMapFunction(doc) {
+ emit(doc.name);
+}
+```
+
+This is functionally equivalent to the SQL index given above. What it essentially says is: "for each document in the database, emit its name as a key."
+
+And since it's just JavaScript, you're allowed to get as fancy as you want here:
+
+```js
+function myMapFunction(doc) {
+ if (doc.type === 'pokemon') {
+ if (doc.name === 'Pikachu') {
+ emit('Pika pi!');
+ } else {
+ emit(doc.name);
+ }
+ }
+}
+```
+
+Then you can query it:
+
+```js
+// find pokemon with name === 'Pika pi!'
+pouch.query(myMapFunction, {
+ key : 'Pika pi!',
+ include_docs : true
+}).then(function (result) {
+ // handle result
+}).catch(function (err) {
+ // handle errors
+});
+
+// find the first 5 pokemon whose name starts with 'P'
+pouch.query(myMapFunction, {
+ startkey : 'P',
+ endkey : 'P\ufff0',
+ limit : 5,
+ include_docs : true
+}).then(function (result) {
+ // handle result
+}).catch(function (err) {
+ // handle errors
+});
+```
+
+{% include alert/start.html variant="info"%}
+
+The pagination options for query() – i.e., startkey/endkey/key/keys/skip/limit/descending – are exactly the same as with allDocs(). For a guide to pagination, read the Bulk operations guide or Pagination strategies with PouchDB.
+
+{% include alert/end.html %}
+
+#### Reduce functions
+
+As for _reduce_ functions, there are a few handy built-ins that do aggregate operations (`'_sum'`, `'_count'`, and `'_stats'`), and you can typically steer clear of trying to write your own:
+
+```js
+// emit the first letter of each pokemon's name
+const myMapReduceFun = {
+ map: function (doc) {
+ emit(doc.name.charAt(0));
+ },
+ reduce: '_count'
+};
+// count the pokemon whose names start with 'P'
+pouch.query(myMapReduceFun, {
+ key: 'P', reduce: true, group: true
+}).then(function (result) {
+ // handle result
+}).catch(function (err) {
+ // handle errors
+});
+```
+
+If you're adventurous, though, you should check out the [CouchDB documentation](http://couchdb.readthedocs.org/en/latest/couchapp/views/intro.html) or the [PouchDB documentation]({{ site.baseurl }}/api.html#query_database) for details on reduce functions.
+
+{% include anchor.html title="Avoiding map/reduce" hash="avoiding-map-reduce" %}
+
+The map/reduce API is complex, and it can be computationally expensive because it requires building up an entirely new index. Therefore, it's good to know some tricks for avoiding the map/reduce API when you don't need it:
+
+1. If you can use `allDocs()` or `changes()` instead of the `query()` API, do it!
+2. If your query is simple enough that you can use `find()`, use that instead.
+3. Read the [12 tips for better code with PouchDB](/2014/06/17/12-pro-tips-for-better-code-with-pouchdb.html), especially the tip to "use and abuse your doc _ids."
+4. If your data is highly relational, try the [relational-pouch](https://github.com/nolanlawson/relational-pouch) plugin, which follows this advice, and only uses `_id` and `allDocs()` under the hood.
+
+{% include anchor.html title="Related API documentation" hash="related-api-documentation" %}
+
+* [query()](/api.html#query_database)
+* [viewCleanup()](/api.html#view_cleanup)
+
+{% include anchor.html title="Next" hash="next" %}
+
+Now that we've learned how to map reduce, map reuse, and map recycle, let's move on to `destroy()` and `compact()`.
diff --git a/docs/version/latest/guides/replication.md b/docs/version/latest/guides/replication.md
new file mode 100644
index 0000000000..4f6a724205
--- /dev/null
+++ b/docs/version/latest/guides/replication.md
@@ -0,0 +1,194 @@
+---
+index: 10
+layout: guide.html
+title: Replication
+sidebar: guides_nav.html
+---
+
+PouchDB and CouchDB were designed for one main purpose: **sync**. Jason Smith has [a great quote](http://nodeup.com/thirtyseven) about this:
+
+> The way I like to think about CouchDB is this: CouchDB is bad at everything, *except syncing*. And it turns out that's the most important feature you could ever ask for, for many types of software."
+
+When you first start using CouchDB, you may become frustrated because it doesn't work quite like other databases. Unlike most databases, CouchDB requires you to manage revisions (`_rev`), which can be tedious.
+
+However, CouchDB was designed with sync in mind, and this is exactly what it excels at. Many of the rough edges of the API serve this larger purpose. For instance, managing your document revisions pays off in the future, when you eventually need to start dealing with conflicts.
+
+{% include anchor.html title="CouchDB sync" hash="couchdb-sync" %}
+
+CouchDB sync has a unique design. Rather than relying on a master/follower architecture, CouchDB
+supports a **multi-master** architecture. You can think of this as a system where any node can be written to or read from, and where you don't have to care which one is the "master" and which one is the "follower." In CouchDB's egalitarian world, every citizen is as worthy as another.
+
+
+ {% include img.html src="offline_replication.gif" alt="Offline replication with CouchDB." %}
+
+
(Thanks to IBM for the image: http://www.ibm.com/developerworks/library/wa-couchdb/)
+
+
+
+When you use PouchDB, CouchDB, and other members of the Couch family, you
+don't have to worry which database is the "single source of truth." They all are. According to the CAP theorem, a database can only have at most 2 of 3 properties: Consistency, Availability, or Partition-Tolerance. Typical relational databases such as MySQL are CP, which means they are consistent and tolerant to node partitions, at the expense of availability. CouchDB is an AP database, meaning that it's **P**artition-Tolerant,
+every node is **A**vailable at all times, but it's only eventually **C**onsistent.
+
+To illustrate, imagine a multi-node architecture with CouchDB servers spread across several continents. As long as you're willing to wait, the data will eventually flow
+from Australia to Europe to North America to wherever. Users around the world running PouchDB in their browsers or [Couchbase Lite](https://github.com/couchbase/couchbase-lite-ios)/[Cloudant Sync](https://github.com/cloudant/CDTDatastore) in their smartphones experience the
+same privileges. The data won't show up instantaneously, but depending on the Internet connection speed, it's usually close enough to real-time.
+
+In cases of conflict, CouchDB will choose an arbitrary winner that every node can agree upon deterministically. However, conflicts are still stored in the **revision tree** (similar to a Git history tree), which means that app developers can either surface the conflicts to the user, or just ignore them.
+
+In this way, CouchDB replication "just works."
+
+{% include anchor.html title="Setting up sync" hash="setting-up-sync" %}
+
+As you already know, you can create either local PouchDBs:
+
+```js
+const localDB = new PouchDB('mylocaldb')
+```
+
+or remote PouchDBs:
+
+```js
+const remoteDB = new PouchDB('http://localhost:5984/myremotedb')
+```
+
+This pattern comes in handy when you want to share data between the two.
+
+The simplest case is **unidirectional replication**, meaning you just want one database to mirror its changes to a second one. Writes to the second database, however, will not propagate back to the master database.
+
+To perform unidirectional replication, you simply do:
+
+```js
+localDB.replicate.to(remoteDB).on('complete', function () {
+ // yay, we're done!
+}).on('error', function (err) {
+ // boo, something went wrong!
+});
+```
+
+Congratulations, all changes from the `localDB` have been replicated to the `remoteDB`.
+
+However, what if you want **bidirectional replication**? You could do:
+
+```js
+localDB.replicate.to(remoteDB);
+localDB.replicate.from(remoteDB);
+```
+
+However, to make things easier for your poor tired fingers, PouchDB has a shortcut API:
+
+```js
+localDB.sync(remoteDB);
+```
+
+These two code blocks above are equivalent. And the `sync` API supports all the same events as the `replicate` API:
+
+```js
+localDB.sync(remoteDB).on('complete', function () {
+ // yay, we're in sync!
+}).on('error', function (err) {
+ // boo, we hit an error!
+});
+```
+
+{% include anchor.html title="Live replication" hash="live–replication" %}
+
+Live replication (or "continuous" replication) is a separate mode where changes are propagated between the two databases as the changes occur. In other words, normal replication happens once, whereas live replication happens in real time.
+
+To enable live replication, you simply specify `{live: true}`:
+
+```js
+localDB.sync(remoteDB, {
+ live: true
+}).on('change', function (change) {
+ // yo, something changed!
+}).on('error', function (err) {
+ // yo, we got an error! (maybe the user went offline?)
+});
+```
+
+However, there is one gotcha with live replication: what if the user goes offline? In those cases, an error will be thrown and replication will stop.
+
+You can allow PouchDB to automatically handle this error, and retry until the connection is re-established, by using the `retry` option:
+
+```js
+localDB.sync(remoteDB, {
+ live: true,
+ retry: true
+}).on('change', function (change) {
+ // yo, something changed!
+}).on('paused', function (info) {
+ // replication was paused, usually because of a lost connection
+}).on('active', function () {
+ // replication was resumed
+}).on('error', function (err) {
+ // totally unhandled error (shouldn't happen)
+});
+```
+
+This is ideal for scenarios where the user may be flitting in and out of connectivity, such as on mobile devices.
+
+{% include anchor.html title="Canceling replication" hash="canceling—replication" %}
+
+Sometimes, you may want to manually cancel replication – for instance, because the user logged out. You can do so by calling `cancel()` and then waiting for the `'complete'` event:
+
+```js
+const syncHandler = localDB.sync(remoteDB, {
+ live: true,
+ retry: true
+});
+
+syncHandler.on('complete', function (info) {
+ // replication was canceled!
+});
+
+syncHandler.cancel(); // <-- this cancels it
+```
+
+The `replicate` API also supports canceling:
+
+```js
+const replicationHandler = localDB.replicate.to(remoteDB, {
+ live: true,
+ retry: true
+});
+
+replicationHandler.on('complete', function (info) {
+ // replication was canceled!
+});
+
+replicationHandler.cancel(); // <-- this cancels it
+```
+
+{% include anchor.html title="Deleting replicated databases" hash="delete-during-replication" %}
+-----
+
+One thing to note about replication is that it tracks the data within a database, not the database itself. If you [`destroy()`](/api.html#delete_database) a database that is being replicated to, the next time the replication starts it will transfer all of the data again, recreating the database to the state it was before it was `destroyed`. If you want the data within the database to be deleted you will need to delete via [`remove()`](/api.html#delete_document) or [`bulkDocs()`](/api.html#batch_create). The [pouchdb-erase](https://github.com/marten-de-vries/pouchdb-erase) plugin can help you remove the entire contents of a database.
+
+{% include anchor.html title="Fancy replication" hash="fancy-replication" %}
+-----
+
+Any PouchDB object can replicate to any other PouchDB object. So for instance, you can replicate two remote databases, or two local databases. You can also replicate from multiple databases into a single one, or from a single database into many others.
+
+This can be very powerful, because it enables lots of fancy scenarios. For example:
+
+1. You have an [in-memory PouchDB]({{ site.baseurl }}/adapters.html#pouchdb_in_the_browser) that replicates with a local PouchDB, acting as a cache.
+2. You have many remote CouchDB databases that the user may access, and they are all replicated to the same local PouchDB.
+3. You have many local PouchDB databases, which are mirrored to a single remote CouchDB as a backup store.
+
+The only limits are your imagination and your disk space.
+
+{% include alert/start.html variant="warning" %}
+
+When you replicate between two remote databases, the changes flow through PouchDB. If this is not what you want, then you should POST directly to the CouchDB _replicate endpoint, as described in the CouchDB replication guide.
+
+{% include alert/end.html %}
+
+
+{% include anchor.html title="Related API documentation" hash="related-api-documentation" %}
+
+* [replication()](/api.html#replication)
+* [sync()](/api.html#sync)
+
+{% include anchor.html title="Next" hash="next" %}
+
+Now that we have a grasp on replication, let's talk about an inconvenient fact of life: conflicts.
diff --git a/docs/version/latest/guides/setup-couchdb.md b/docs/version/latest/guides/setup-couchdb.md
new file mode 100644
index 0000000000..19c962c299
--- /dev/null
+++ b/docs/version/latest/guides/setup-couchdb.md
@@ -0,0 +1,103 @@
+---
+index: 2
+layout: guide.html
+title: Setting up CouchDB
+sidebar: guides_nav.html
+---
+
+{% include anchor.html title="CouchDB: PouchDB's older sibling" hash="couchdb-pouchdbs-older-sibling" %}
+
+One of the main benefits of learning PouchDB is that it's exactly the same as CouchDB. In fact, PouchDB is a shameless plagiarist: all of the API methods are the same, with only slight modifications to make it more JavaScript-y.
+
+For instance, in CouchDB you would fetch all documents using:
+
+```bash
+/db/_all_docs?include_docs=true
+```
+
+In PouchDB this becomes:
+
+```js
+db.allDocs({include_docs: true})
+```
+
+The APIs are the same, and the semantics are the same.
+
+In the following examples, we will set up CouchDB and talk to it using a tool you're already familiar with: your browser.
+
+{% include anchor.html title="Installing CouchDB" hash="installing-couchdb" %}
+
+If you are on a Debian flavor of Linux (Ubuntu, Mint, etc.), you can install CouchDB as follows.
+
+First, [enable the CouchDB package repository](https://docs.couchdb.org/en/stable/install/unix.html#enabling-the-apache-couchdb-package-repository) on your machine:
+
+```bash
+$ sudo apt update && sudo apt install -y curl apt-transport-https gnupg
+$ curl https://couchdb.apache.org/repo/keys.asc | gpg --dearmor | sudo tee /usr/share/keyrings/couchdb-archive-keyring.gpg >/dev/null 2>&1
+source /etc/os-release
+$ echo "deb [signed-by=/usr/share/keyrings/couchdb-archive-keyring.gpg] https://apache.jfrog.io/artifactory/couchdb-deb/ ${VERSION_CODENAME} main" \
+ | sudo tee /etc/apt/sources.list.d/couchdb.list >/dev/null
+```
+
+Next, update your package lists and install CouchDB:
+
+```bash
+$ sudo apt-get update
+$ sudo apt-get install -y couchdb
+```
+
+If you are on a Mac or Windows you should install the official binaries from [the CouchDB web site](https://couchdb.apache.org/#download).
+
+#### A CouchDB alternative: PouchDB Server
+
+If you have trouble installing CouchDB, you can also install PouchDB Server, which is a drop-in replacement for CouchDB that uses PouchDB under the hood:
+
+```bash
+$ npm install -g pouchdb-server
+$ pouchdb-server --port 5984
+```
+
+PouchDB Server is currently experimental, and we do not recommend it for production environments.
+
+{% include anchor.html title="Verify your installation" hash="verify-your–installation" %}
+
+Once CouchDB is installed, it should be running at `localhost:5984`. To verify, you can open up your terminal and type
+
+```bash
+$ curl localhost:5984
+```
+
+You should see something like:
+
+```js
+{"couchdb":"Welcome","version":"2.2.0",...}
+```
+
+Next, open up [http://localhost:5984/_utils/](http://localhost:5984/_utils/) in your browser.
+
+If you see a screen like the following, then you are ready to rock and roll with CouchDB:
+
+
+{% include img.html src="fauxton.png" alt="Fauxton interface" %}
+
+{% include anchor.html title="Set up CORS" hash="set-up-cors" %}
+
+[CORS](https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS) is a web technology that allows web sites to use resources from another domain. You will want to enable this in your CouchDB before continuing, because otherwise PouchDB will not work unless it's served from exactly the same domain as CouchDB.
+
+Enabling CORS is easy. Just install this handy script:
+
+```bash
+$ npm install -g add-cors-to-couchdb
+```
+
+And run it:
+
+```bash
+$ add-cors-to-couchdb
+```
+
+If you installed PouchDB Server, CORS is enabled by default, and this step is not necessary.
+
+{% include anchor.html title="Next" hash="next" %}
+
+Now that you have CouchDB installed, let's install PouchDB.
diff --git a/docs/version/latest/guides/setup-pouchdb.md b/docs/version/latest/guides/setup-pouchdb.md
new file mode 100644
index 0000000000..baea6a4551
--- /dev/null
+++ b/docs/version/latest/guides/setup-pouchdb.md
@@ -0,0 +1,97 @@
+---
+index: 3
+layout: guide.html
+title: Setting up PouchDB
+sidebar: guides_nav.html
+---
+
+
+Installing PouchDB is easy. There are a few different ways to do it:
+
+{% include anchor.html title="Direct download" hash="direct-download" %}
+
+Download the latest **pouchdb-{{site.version}}.min.js** from the big green button above. Then in your `index.html`:
+
+```html
+
+```
+
+{% include anchor.html title="Bower" hash="bower" %}
+
+Run this on the command line:
+
+```bash
+$ bower install pouchdb
+```
+
+Then in your `index.html`:
+
+```html
+
+```
+
+{% include anchor.html title="npm" hash="npm" %}
+
+Run this on the command line:
+
+```bash
+$ npm install pouchdb
+```
+
+Then in your `index.html`:
+
+```html
+
+```
+
+{% include anchor.html title="jsdelivr CDN" hash="jsdelivr-cdn" %}
+
+Add this to your `index.html`:
+
+```html
+
+```
+
+{% include anchor.html title="Node.js" hash="nodejs" %}
+
+Run this on the command line:
+
+```bash
+$ npm install pouchdb
+```
+
+Then in your JavaScript:
+
+```js
+const PouchDB = require('pouchdb');
+```
+
+{% include anchor.html title="With TypeScript" hash="typescript" %}
+
+Run this on the command line:
+
+```bash
+$ npm install pouchdb @types/pouchdb
+```
+
+In your `tsconfig.json` activate `allowSyntheticDefaultImports`:
+
+```json
+{
+ "compilerOptions": {
+ "allowSyntheticDefaultImports": true
+ }
+}
+```
+
+Then in your TypeScript:
+
+```typescript
+import PouchDB from 'pouchdb';
+```
+
+You can install a plugin (provided there is a [type definition for it in npm](https://www.npmjs.com/search?q=scope:types%20pouchdb)), import it in the same way and then pass the imported name to `PouchDB.plugin()` method just as you would do in JavaScript.
+
+{% include anchor.html title="Next" hash="next" %}
+
+Now that you have PouchDB installed, let's start working with databases.
diff --git a/docs/version/latest/guides/updating-deleting.md b/docs/version/latest/guides/updating-deleting.md
new file mode 100644
index 0000000000..ba3b8b6c82
--- /dev/null
+++ b/docs/version/latest/guides/updating-deleting.md
@@ -0,0 +1,145 @@
+---
+index: 7
+layout: guide.html
+nav: Updating/deleting documents
+title: Updating and deleting documents
+sidebar: guides_nav.html
+---
+
+As we saw in the past two chapters, working with PouchDB documents can be tricky, because you have to manage the revision identifier `_rev`.
+
+Now that we understand promises, though, there are few techniques we can use to make our code more elegant and readable.
+
+{% include anchor.html title="Creating a default document" hash="creating-a–default-document" %}
+
+Often in our code, we'll want to `get()` a document, and if it doesn't exist, we want to create some default.
+
+For instance, let's say we have a configuration object. We want to provide some reasonable defaults for our config:
+
+```js
+{
+ _id: 'config',
+ background: 'blue',
+ foreground: 'white',
+ sparkly: 'false'
+}
+```
+
+This is a pretty good default setting! So let's write the code to set it as our default.
+
+Thankfully, promises make this rather easy:
+
+```js
+db.get('config').catch(function (err) {
+ if (err.name === 'not_found') {
+ return {
+ _id: 'config',
+ background: 'blue',
+ foreground: 'white',
+ sparkly: 'false'
+ };
+ } else { // hm, some other error
+ throw err;
+ }
+}).then(function (configDoc) {
+ // sweet, here is our configDoc
+}).catch(function (err) {
+ // handle any errors
+});
+```
+
+This code is doing the following:
+
+* Try to `get()` a doc with `_id` equal to `'config'`
+* If it doesn't find it, return the default doc
+* Otherwise, you'll just get back the existing document
+
+You can see **[a live example](http://bl.ocks.org/nolanlawson/0a01d466b2d331cf7e25)** of this code.
+
+{% include anchor.html title="Why must we dance this dance?" hash="why-must-we-dance-this-dance" %}
+
+A common question from new PouchDB/CouchDB users is: why do we have to deal with `_rev` at all? Why can't I just `put()` the document without providing a `_rev`?
+
+The answer is: because `_rev`s are what makes sync work so well. PouchDB asks for a little upfront effort with managing document revisions, so that later on, sync is a breeze.
+
+In fact, you are probably already familiar with a system that forces you to go through a similar dance. This system is called [Git](http://www.git-scm.com/).
+
+PouchDB and CouchDB's document revision structure is very similar to Git's. In fact, each document's revision history is stored as a tree (exactly like Git), which allows you to handle conflicts when any two databases get out of sync.
+
+```
+rev 3-a rev 3-b
+ \___/
+ |
+ rev 2
+ |
+ rev 1
+```
+
+Conflicts will be discussed later in this guide. For now, you can think of revisions as being a single lineage:
+
+```
+ rev 4
+ |
+ rev 3
+ |
+ rev 2
+ |
+ rev 1
+```
+
+{% include anchor.html title="Deleting documents" hash="deleting-documents" %}
+
+When you `remove()` a document, it's not really deleted; it just gets a `_deleted` attribute added to it.
+
+That is, the database saves a tombstone at the end of the revision tree.
+
+```
+{_id: 'foo', _rev: '4-z', _deleted: true}
+ |
+{_id: 'foo', _rev: '3-y'}
+ |
+{_id: 'foo', _rev: '2-x'}
+ |
+{_id: 'foo', _rev: '1-w'}
+```
+
+There are three ways of deleting a document, which are all equivalent:
+
+1) You can call `db.remove(doc)`:
+
+```js
+db.get('mydoc').then(function (doc) {
+ return db.remove(doc);
+});
+```
+
+2) You can call `db.remove(doc._id, doc._rev)`:
+
+```js
+db.get('mydoc').then(function (doc) {
+ return db.remove(doc._id, doc._rev);
+});
+```
+
+3) You can call `db.put(doc)` with `_deleted` set to `true`:
+
+```js
+db.get('mydoc').then(function (doc) {
+ doc._deleted = true;
+ return db.put(doc);
+});
+```
+
+Of course, you will want to add `catch()` to the end of all these, unless you like to live dangerously.
+
+You can see **[a live example](http://bl.ocks.org/nolanlawson/b2049ad69308e92f15bc)** of this code.
+
+{% include anchor.html title="Related API documentation" hash="related-api-documentation" %}
+
+* [get()](/api.html#fetch_document)
+* [put()](/api.html#create_document)
+* [remove()](/api.html#delete_document)
+
+{% include anchor.html title="Next" hash="next" %}
+
+Now that we understand how to update and delete documents, let's do it in bulk.
diff --git a/docs/version/latest/learn.md b/docs/version/latest/learn.md
new file mode 100644
index 0000000000..d23ac36335
--- /dev/null
+++ b/docs/version/latest/learn.md
@@ -0,0 +1,38 @@
+---
+layout: 2ColLeft.html
+title: About PouchDB
+sidebar: ./nav.html
+---
+
+PouchDB is an **in-browser database** that allows applications to save data locally, so that users can enjoy all the features of an app even when they're offline. Plus, the data is synchronized between clients, so users can stay up-to-date wherever they go.
+
+PouchDB also runs in **Node.js** and can be used as a direct interface to **CouchDB**-compatible servers. The API works the same in every environment, so you can spend less time worrying about browser differences, and more time writing clean, consistent code.
+
+PouchDB is a free open-source project, written in JavaScript and driven by our [wonderful community](https://github.com/apache/pouchdb/graphs/contributors). If you want to get involved, then check out the [contributing guide](https://github.com/apache/pouchdb/blob/master/CONTRIBUTING.md).
+
+{% include anchor.html class="h3" title="Browser Support" hash="browser_support" %}
+
+PouchDB supports all modern browsers, using [IndexedDB][] under the hood and falling back to [WebSQL][] where IndexedDB isn't supported. It is [fully tested](https://github.com/apache/pouchdb/actions) and supported in:
+
+ * Firefox 29+ (Including Firefox OS and Firefox for Android)
+ * Chrome 30+
+ * Safari 5+
+ * Internet Explorer 10+
+ * Opera 21+
+ * Android 4.0+
+ * iOS 7.1+
+ * Windows Phone 8+
+
+PouchDB also runs in [Cordova/PhoneGap](https://github.com/nolanlawson/pouchdb-phonegap-cordova), [NW.js](https://github.com/nolanlawson/pouchdb-nw), [Electron](https://github.com/nolanlawson/pouchdb-atom-shell), and [Chrome apps](https://github.com/nolanlawson/pouchdb-chrome-app). It is framework-agnostic, and you can use it with Angular, React, Ember, Backbone, or your framework of choice. There are [many adapters]({{ site.baseurl }}/external.html#framework_adapters), or you can just use PouchDB as-is.
+
+PouchDB requires a modern ES5 environment, so if you need to support older browsers (IE <10, Android <4.0, Opera Mini), then you should include the [es5-shim](https://github.com/es-shims/es5-shim) library. You can also use the [LocalStorage and in-memory adapters](/adapters.html#pouchdb_in_the_browser), or fall back to a live CouchDB.
+
+{% include anchor.html class="h3" title="Node.js" hash="node_js" %}
+
+In Node.js, PouchDB uses [LevelDB][] under the hood, and also supports [many other backends](/adapters.html#pouchdb_in_node_js) via the [LevelUP ecosystem](https://github.com/rvagg/node-levelup).
+
+PouchDB can also run as its own CouchDB-compatible web server, using [PouchDB Server](https://github.com/pouchdb/pouchdb-server).
+
+[IndexedDB]: http://caniuse.com/#feat=indexeddb
+[WebSQL]: http://caniuse.com/#feat=sql-storage
+[LevelDB]: https://github.com/google/leveldb
diff --git a/docs/version/latest/offline.md b/docs/version/latest/offline.md
new file mode 100644
index 0000000000..105fbc1a23
--- /dev/null
+++ b/docs/version/latest/offline.md
@@ -0,0 +1,11 @@
+---
+layout: default.html
+title: Looks like you're offline...
+edit: false
+---
+
+
+
+
You need to be online to see this page, once you've seen it once it'll be available offline in the future!
+
+
diff --git a/docs/version/latest/users.md b/docs/version/latest/users.md
new file mode 100644
index 0000000000..b9aed7570a
--- /dev/null
+++ b/docs/version/latest/users.md
@@ -0,0 +1,132 @@
+---
+layout: 2ColLeft.html
+title: Who's using PouchDB?
+sidebar: ./nav.html
+---
+
+A list of known products and services that are using PouchDB.
+## Newt
+
+{% include img.html width=100 src="newt.png" alt="Newt" %}
+
+[Newt](https://newt.to) lets you read, listen and write thousands of books across devices natively. It runs on Windows, Linux, macOS, Android, iOS & web. Its backed is powered by CouchDB and it uses PouchDB on the web client, desktop client and mobile native app for offline access, live sync, revisions and much more.
+
+## BikeCommute
+
+[BikeCommute](https://github.com/autonome/bikecommute) is a FirefoxOS app that registers an NFC tag to track bike commuters in the Mozilla Portland office. Built using Famo.us, PouchDB, and CouchDB. See [a video of it in action](https://youtu.be/3BVZYcQ-TYA) or [read about it on Mozilla Hacks](https://hacks.mozilla.org/2014/11/nfc-in-firefox-os/).
+
+## Cloudwall
+
+{% include img.html width=200 src="cloudwall.png" alt="Cloudwall" %}
+
+[Cloudwall](http://cloudwall.me/) is an operating system for noBackend webapps, based on CouchDB and PouchDB.
+
+## Cozy Cloud
+
+{% include img.html width=125 src="cozy.png" alt="Cozy Cloud" %}
+
+[Cozy](https://cozy.io/en/) is a personal cloud that you can host, customize, and fully control. It syncs contacts, calendars, and files between your personal devices and server. Under the hood, it leverages CouchDB and PouchDB.
+
+## Delta
+[Delta](https://github.com/octavore/delta) is a command-line utility for text diffs. View split diffs in the browser with syntax highlighting, or in the command-line using the --cli flag.
+
+## DevITJobs
+[DevITJobs](http://devitjobs.com) is a job board for the tech industry with mandatory salary ranges & tech stacks.
+
+## eHealth Africa
+
+{% include img.html width=200 src="ehealth_africa.png" alt="eHealth Africa" %}
+
+[eHealth Africa](http://ehealthafrica.org/) is an American-Nigerian NGO specialising in the development and deployment of tech for health. To tackle the Ebola outbreak, they built [mobile apps and dashboards](https://github.com/eHealthAfrica) to help track the spread of infection in the field. The combination of CouchDB and PouchDB enabled these apps to work consistently despite the extreme network unreliability of sub-saharan Africa.
+
+## Financier
+
+[Financier](https://financier.io) is a freemium personal budgeting app that uses PouchDB to store your budget data. The paid version syncs data with CouchDB 2 for data persistence and sharing across devices. The app is fully integrated with the PouchDB changes feed for instant updates. More about the stack is available in [humans.txt](https://app.financier.io/humans.txt).
+
+## GRADEpro GDT
+
+{% include img.html width=150 src="gradepro.png" alt="GRADEpro GDT" href="http://gradepro.org/" %}
+
+[GRADEpro GDT](http://gradepro.org/) is an easy to use, all‐in‐one web solution to summarize and present information for healthcare decision making. PouchDB is instrumental for both the collaboration features of GRADEpro (so many users can work on the same data simultaneously) and implementation of the offline mode. The tool is used in more than 135 countries, some of which have poor Internet connectivity, as well as by scientists, who are frequent travelers and need to use the application e.g. on a plane.
+
+## Hoodie
+
+{% include img.html width=150 src="HoodieLogo.png" alt="Hoodie" href="http://hood.ie/" %}
+
+[Hoodie](http://hood.ie/) provides a complete backend solution for your frontend code. It helps you develop your web application fast and easy. Hoodie-based apps are [offline-first](http://offlinefirst.org/) so they are usable anytime. Just plug Hoodie’s API into your frontend code, and your app is ready.
+
+## HospitalRun
+
+{% include img.html width=150 src="hospitalrun-logo.svg" alt="HospitalRun" href="http://hospitalrun.io/" %}
+
+[HospitalRun](http://hospitalrun.io/) is an open source software product designed specifically for developing world hospitals, making usability the key requirement. Using PouchDB and offline-first design, HospitalRun allows records to be carried to remote clinics, functioning when there is no Internet, and syncing when there is.
+
+## Local NPM
+
+[Local NPM](https://github.com/nolanlawson/local-npm) allows you to easily set up a local NPM server that caches data from the real NPM and updates in realtime. The goal is to cut down network request time by moving data closer to the client. Built using Node.js, LevelDB, and PouchDB.
+
+## Lullabot
+
+[Lullabot](https://www.lullabot.com) is an interactive strategy, design, and development company. We create delightful experiences using Drupal and open source technologies. Our main website is using Drupal as the back-end for content management, CouchDB with PouchDB for the API, and ReactJS for the front-end.
+
+## MBTA Alerts
+
+{% include img.html width=100 src="mbta_alerts.jpeg" alt="MBTA Alerts" %}
+
+[MBTA Alerts](https://twitter.com/MBTA_Alerts) is a Twitter bot that thousands of Bostonians depend upon to be notified of delays in the MBTA transit system. Uses Node.js, PouchDB, and CouchDB.
+
+## Medic Mobile
+
+{% include img.html src="medic-mobile.png" alt="Medic Mobile" %}
+
+[Medic Mobile](http://medic.org) is a software toolkit that combines smart messaging, decision support, easy data gathering and management, and health system analytics. Thanks to PouchDB our tools are offline first so workers can file reports wherever they are and sync when back online.
+
+## MoneyTracker.cc
+
+[MoneyTracker](https://moneytracker.cc/) is an open-source personal finances tracking web app. This app can work offline on desktop, tablet and mobile.
+Data is stored locally on device in PouchDB database and can be synced to the cloud.
+
+## NPM Browser
+
+[NPM Browser](https://github.com/pouchdb/npm-browser) is a fully offline cache of NPM packages, which runs in your browser, using Angular.js, PouchDB, and the pouchdb-load plugin.
+
+## Pokedex.org
+
+{% include img.html width=100 src="pokedexorg-logo.png" alt="Pokedex.org logo" %}
+
+[Pokedex.org](https://github.com/nolanlawson/pokedex.org) is a progressive web app, powered by ServiceWorker, PouchDB, virtual-dom, and web workers.
+
+## Quizster
+
+{% include img.html width=100 src="quizster.svg" alt="Quizster" href="https://quizster.co" %}
+
+[Quizster](https://quizster.co) is a photo-based submission and feedback system. Quizster uses PouchDB to allow real-time communication between students and teachers.
+
+## Squarespace Blog
+
+{% include img.html width=100 src="squarespace_blog.png" alt="Squarespace Blog" %}
+
+Squarespace Blog is an [Android](https://play.google.com/store/apps/details?id=com.squarespace.android.blog) and [iOS app](https://itunes.apple.com/us/app/squarespace-blog/id715084234) that gives you the tools you need to write and edit posts on multiple Squarespace websites. It uses PouchDB attachments inside a WebView for fast offline images.
+
+## Story-writer
+
+Story-writer is a free Markdown editor, with a [web version](http://markdown.xiaoshujiang.com) and [NW.js client version](http://soft.xiaoshujiang.com), built using Node.js, NW.js, LevelDB, and PouchDB.
+
+## Thali project
+
+[Thali](http://thaliproject.org/) is a Microsoft-sponsored open-source platform for creating apps that exploit the power of personal devices and put people in control of their data. It uses Cordova, PouchDB, OpenSSL, and Tor.
+
+## StudyMD
+
+{% include img.html width=100 src="studymd.png" alt="StudyMD" %}
+
+[StudyMD](https://github.com/jotron/StudyMD) transforms your markdown files into flashcards. It's an electronjs app based on reactjs. PouchDB is used in Node.js for storing the flashcards in a LevelDB database.
+
+## YLD
+
+[YLD](http://www.yld.io) is a Node.js software engineering, consulting and training company. We partner with enterprises to strengthen their software engineering culture and create the agility necessary to compete in today’s market, and are responsible for some of the largest Node.js solutions in production today. PouchDB allows us to create responsive, resilient and sync-enabled web applications.
+
+## FactoryTalk TeamONE
+
+{% include img.html src="TeamONE.png" alt="FactoryTalk TeamONE" %}
+[FactoryTalk® TeamONE™](http://33seconds.io) delivers one new experience and helps industrial teams improve productivity. Created by [Rockwell Automation](http://www.rockwellautomation.com/). We're using PouchDB for offline access and team synchronization. Looking to use the [Thali project](http://thaliproject.org/) (also a PouchDB client listed here) to enable true peer-to-peer connectivity. Connect with us on twitter ([@ROKTeamONE](https://twitter.com/rokteamone).)
From 41191088563d62533d684865f1100bc4fb369e48 Mon Sep 17 00:00:00 2001
From: Alex Feyerke
Date: Mon, 11 May 2026 10:17:15 +0200
Subject: [PATCH 03/13] fix: sidebar navigations need to be versioned too
---
docs/_includes/nav.html | 10 ----------
docs/_includes/nav_item.html | 2 +-
docs/version/7.3.0/nav.html | 16 ++++++++++++++++
docs/version/8.0.0/nav.html | 16 ++++++++++++++++
docs/version/9.0.0/nav.html | 16 ++++++++++++++++
docs/version/latest/nav.html | 16 ++++++++++++++++
6 files changed, 65 insertions(+), 11 deletions(-)
delete mode 100644 docs/_includes/nav.html
create mode 100644 docs/version/7.3.0/nav.html
create mode 100644 docs/version/8.0.0/nav.html
create mode 100644 docs/version/9.0.0/nav.html
create mode 100644 docs/version/latest/nav.html
diff --git a/docs/_includes/nav.html b/docs/_includes/nav.html
deleted file mode 100644
index 6ade7a0232..0000000000
--- a/docs/_includes/nav.html
+++ /dev/null
@@ -1,10 +0,0 @@
-{% include nav_item.html path="/learn.html" text="About" %}
-{% include nav_item.html path="/download.html" text="Download" %}
-{% include nav_item.html path="/users.html" text="Who's using PouchDB?" %}
-{% include nav_item.html path="/getting-started.html" text="Get Started Guide" %}
-{% include nav_item.html path="/api.html" text="API" %}
-{% include nav_item.html path="/adapters.html" text="Adapters" %}
-{% include nav_item.html path="/custom.html" text="Custom Builds" %}
-{% include nav_item.html path="/external.html" text="Plugins" %}
-{% include nav_item.html path="/faq.html" text="FAQ" %}
-{% include nav_item.html path="/errors.html" text="Common Errors" %}
diff --git a/docs/_includes/nav_item.html b/docs/_includes/nav_item.html
index c857389cbc..0012416a51 100644
--- a/docs/_includes/nav_item.html
+++ b/docs/_includes/nav_item.html
@@ -1,3 +1,3 @@
diff --git a/docs/_includes/oldDocsWarning.html b/docs/_includes/oldDocsWarning.html
new file mode 100644
index 0000000000..627b1adbad
--- /dev/null
+++ b/docs/_includes/oldDocsWarning.html
@@ -0,0 +1,13 @@
+
+{% if version and version != versions.stable %}
+
\ No newline at end of file
diff --git a/docs/src/less/pouchdb/pouchdb.less b/docs/src/less/pouchdb/pouchdb.less
index 5143fc908e..6a7c818bdf 100644
--- a/docs/src/less/pouchdb/pouchdb.less
+++ b/docs/src/less/pouchdb/pouchdb.less
@@ -27,6 +27,7 @@
@import "@{bootstrap}/dropdowns.less";
@import "@{bootstrap}/pager.less";
@import "@{bootstrap}/tooltip.less";
+@import "@{bootstrap}/dropdowns.less";
// Utility classes
@import "@{bootstrap}/responsive-utilities.less";
@@ -48,3 +49,4 @@
@import "icons.less";
@import "block.less";
@import "icon-edit.less";
+@import "version-switcher.less";
diff --git a/docs/src/less/pouchdb/version-switcher.less b/docs/src/less/pouchdb/version-switcher.less
new file mode 100644
index 0000000000..2f9d3a700b
--- /dev/null
+++ b/docs/src/less/pouchdb/version-switcher.less
@@ -0,0 +1,11 @@
+.version-switcher {
+ position: fixed;
+ bottom: 2em;
+ right: 2em;
+ .btn {
+ padding-inline: 1em;
+ }
+ .stable {
+ font-weight: bold;
+ }
+}
\ No newline at end of file
From 46cf6bac95c6d03b3705ed440cf165a6014849f3 Mon Sep 17 00:00:00 2001
From: Alex Feyerke
Date: Mon, 11 May 2026 10:21:58 +0200
Subject: [PATCH 07/13] chore: add a new readme for the website
---
WEBSITE_README.md | 42 ++++++++++++++++++++++++++++++++++++++++++
1 file changed, 42 insertions(+)
create mode 100644 WEBSITE_README.md
diff --git a/WEBSITE_README.md b/WEBSITE_README.md
new file mode 100644
index 0000000000..87631fa76a
--- /dev/null
+++ b/WEBSITE_README.md
@@ -0,0 +1,42 @@
+# Information about editing and building the PouchDB website
+
+- The PouchDB website is an [eleventy](https://www.11ty.dev/) site that lives in the `/docs` folder.
+- You build it with `$npm run build-site`. This will start a dev server of sorts, with the site running at [localhost:4000](htt://localhost:4000). The site will rebuild if you change any template or style files. You still need to refresh the browser yourself. The site is built into `/docs/_site`.
+- Deployment is automatic, whatever lands in the `master` branch is deployed by a github workflow.
+
+## How to update docs for a new feature
+
+When you write a new PouchDB feature, document it in `/docs/version/latest`. This will become part of the `stable` docs as soon as the next release is cut.
+
+## How to update the docs for a new PouchDB release
+
+When you cut a new PouchDB release, complete this checklist:
+
+- [ ] Add the new version to the `/docs/_data/versions.js` file
+- [ ] _Copy_ (not rename) the `/docs/version/latest` folder to `/docs/version/`
+
+That’s it.
+
+## Documentation Structure
+
+The site has versioned docs as of 2026. This means that parts of the site are different depending on which version you’re looking at them for: the `api` section, the `guides` section, and the `learn` section. Basically everything that isn’t the start page or the blog. These docs are written by hand, not autogenerated from the PouchDB source. So whenever you change PouchDB itself, you also need to update the docs.
+
+We define the documented versions in `/docs/_data/versions.js`. This makes two global vars available:
+- `versions.all`: an array of versions we have different docs for, e.g. `['7.0.0', '8.0.0', '9.0.0']`
+- `versions.stable`: the version number of the last release, e.g. `9.0.0`
+
+Versioned docs are kept in `/docs/version/`, and we version like this:
+
+- `latest`: Lives in `/docs/version/latest`. This is a special folder that isn’t tied to a release. All doc updates that concern the _next_ release must be made in here.
+- Versioned docs, for example those for version `10.0.0` live in `/docs/version/10.0.0`. This folder should be created as part of the `10.0.0` release and is simply a copy of `/docs/version/latest`. This means over time there will be `/docs/version/9.0.0`, `/docs/version/10.0.0`, `/docs/version/10.1.0` and so on.
+- `stable`: Another special case. This lives nowhere in the site source, but is generated by Eleventy at build time. It takes the contents of `/docs/version/`, and copies them over to `/`, so the docs at the root of the site are always for the most recent (stable) release. This also preserves any existing links to the unversioned docs we used to have.
+
+This means that if `versions.stable` is `9.0.0`:
+
+- `http://pouchdb.com/api.html` displays `/docs/version/9.0.0/api.html`
+- `http://pouchdb.com/guides/setup-couchdb.html` displays `/docs/version/9.0.0/guides/setup-couchdb.md`
+- `http://pouchdb.com/version/7.0.0/guides/setup-pouchdb.html/` displays `/docs/version/7.0.0/guides/setup-couchdb.md`
+
+To make this re-use of the stable docs possible, we need to jump through two hoops:
+1. Generate the root-level docs from version docs, this is done in `/docs/stable.liquid` and utilises [11ty’s pagination feature](https://www.11ty.dev/docs/pages-from-data/) to iterate over the stable version docs and output them in root.
+2. All sidebar links at root level need to be modified to strip the `/version/` fragment, this is done with the `makeURLForStable` filter in `/eleventy.config.js`.
\ No newline at end of file
From c1b51753e2eac0c41e21078781d9265b95adfa23 Mon Sep 17 00:00:00 2001
From: Alex Feyerke
Date: Mon, 11 May 2026 10:24:12 +0200
Subject: [PATCH 08/13] =?UTF-8?q?chore:=20remove=20all=20purge=20docs=20th?=
=?UTF-8?q?at=20shouldn=E2=80=99t=20be=20in=207.3.0?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
docs/version/7.3.0/api.html | 1 -
docs/version/7.3.0/api/create_database.html | 1 -
docs/version/7.3.0/api/purge.html | 79 ---------------------
3 files changed, 81 deletions(-)
delete mode 100644 docs/version/7.3.0/api/purge.html
diff --git a/docs/version/7.3.0/api.html b/docs/version/7.3.0/api.html
index 0befc9164c..ba4e0a6c92 100644
--- a/docs/version/7.3.0/api.html
+++ b/docs/version/7.3.0/api.html
@@ -33,7 +33,6 @@
{% include ./api/revisions_diff.html %}
{% include ./api/bulk_get.html %}
{% include ./api/close_database.html %}
-{% include ./api/purge.html %}
{% include ./api/events.html %}
{% include ./api/active_tasks.html %}
{% include ./api/defaults.html %}
diff --git a/docs/version/7.3.0/api/create_database.html b/docs/version/7.3.0/api/create_database.html
index d2792ca0fc..d345757901 100644
--- a/docs/version/7.3.0/api/create_database.html
+++ b/docs/version/7.3.0/api/create_database.html
@@ -18,7 +18,6 @@
* `deterministic_revs`: Use a md5 hash to create a deterministic revision number for documents. Setting it to false will mean that the revision number will be a random UUID. Defaults to true.
* `view_update_changes_batch_size`: Specify how many change records will be consumed at a time when rebuilding view indexes when the `query()` method is used. Defaults to 50.
* `view_adapter`: Specify a different adapter to store the view index data.
-* `purged_infos_limit`: Specify how many purged revisions we keep track of. Specifying a low value can result in Pouch not delegating older purges down to views. Defaults to `1000`.
**Options for remote databases:**
diff --git a/docs/version/7.3.0/api/purge.html b/docs/version/7.3.0/api/purge.html
deleted file mode 100644
index 54374a8026..0000000000
--- a/docs/version/7.3.0/api/purge.html
+++ /dev/null
@@ -1,79 +0,0 @@
-{% include anchor.html edit="true" title="Purge a document rev" hash="purge" %}
-
-{% highlight js %}
-db.purge(docId, rev)
-{% endhighlight %}
-
-Purges a specific revision of a document, specified by `docId` and `rev`. `rev` must be a leaf revision.
-
-Purge permanently removes data from the database. Normal deletion with `db.remove()` does not, it only marks the document as `_deleted=true` and creates a new revision. This behaviour ensures that deletes can be replicated across databases, and deleted documents don’t get undeleted by syncing with a database that still has this document.
-
-`db.purge()` is not intended as a regular method for deleting documents, instead, it is meant as an admin function for cases where some secret was erroneously added to the database and must now be removed completely, eg. a credit card or social security number. Purge effectively puts the database in a state where the offending write never happened.
-
-{% include alert/start.html variant="info"%}
-{% markdown %}
-
-**Purge is currently only implemented in the `indexeddb` adapter**.
-
-Using Purge with any other adapter will return an error.
-
-{% endmarkdown %}
-{% include alert/end.html%}
-
-#### Example Usage:
-
-{% include code/start.html id="purge1" type="callback" %}
-{% highlight js %}
-db.purge('mydoc', '6-3a24009a9525bde9e4bfa8a99046b00d',
- function (err, result) {
- if (err) { return console.log(err); }
- // handle result
-);
-{% endhighlight %}
-{% include code/end.html %}
-
-{% include code/start.html id="purge1" type="async" %}
-{% highlight js %}
-try {
- const result = await db.purge('mydoc', '6-3a24009a9525bde9e4bfa8a99046b00d');
- // handle result
-} catch (err) {
- console.log(err);
-}
-{% endhighlight %}
-{% include code/end.html %}
-
-{% include code/start.html id="purge1" type="promise" %}
-{% highlight js %}
-db.purge('mydoc', '6-3a24009a9525bde9e4bfa8a99046b00d')
- .then(function (result) {
- // handle result
- }).catch(function (err) {
- console.log(err);
- });
-{% endhighlight %}
-{% include code/end.html %}
-
-#### Example Response:
-
-{% highlight js %}
- {
- "ok": true,
- "deletedRevs": [
- "6-3a24009a9525bde9e4bfa8a99046b00d",
- "5-df4a81cd21c75c71974d96e88a68fc2f"
- ],
- "documentWasRemovedCompletely": false
- }
-{% endhighlight %}
-
-If the document has no conflicts, purging its only leaf rev deletes the document completely, and `purge()` returns `documentWasRemovedCompletely: true` in its result object.
-
-If the document does have conflicts, purging will remove the specified rev and pick a leaf from another rev branch as the winner. Fetching the doc after the purge will return the updated state of the document with the new winning revision in `_rev`.
-
-**Notes:**
-
-* The `rev` specified for purging must be a leaf, otherwise an error will be returned
-* Purges do not sync, they only apply to the database they are performed on
-* If the document has conflicts, purging a leaf will also remove its ancestors up to the next branching rev, and pick a new winning rev
-* Any attachments referenced in the specified rev will also be deleted
From 42714e8e9329768c14272ab85a9d2999ee8a5f02 Mon Sep 17 00:00:00 2001
From: Alex Feyerke
Date: Mon, 11 May 2026 10:25:05 +0200
Subject: [PATCH 09/13] chore: remove all active_tasks references from 7.3.0
docs
---
docs/version/7.3.0/api.html | 1 -
docs/version/7.3.0/api/active_tasks.html | 45 ------------------------
2 files changed, 46 deletions(-)
delete mode 100644 docs/version/7.3.0/api/active_tasks.html
diff --git a/docs/version/7.3.0/api.html b/docs/version/7.3.0/api.html
index ba4e0a6c92..21f5a7da00 100644
--- a/docs/version/7.3.0/api.html
+++ b/docs/version/7.3.0/api.html
@@ -34,7 +34,6 @@
{% include ./api/bulk_get.html %}
{% include ./api/close_database.html %}
{% include ./api/events.html %}
-{% include ./api/active_tasks.html %}
{% include ./api/defaults.html %}
{% include ./api/plugins.html %}
diff --git a/docs/version/7.3.0/api/active_tasks.html b/docs/version/7.3.0/api/active_tasks.html
deleted file mode 100644
index 673a8a8850..0000000000
--- a/docs/version/7.3.0/api/active_tasks.html
+++ /dev/null
@@ -1,45 +0,0 @@
-{% include anchor.html edit="true" title="List active tasks" hash="active_tasks" %}
-
-{% highlight js %}
-PouchDB.activeTasks.list()
-{% endhighlight %}
-
-List all active database tasks. There are three types of internal tasks: `database_compaction`, `view_indexing`, and `replication`. PouchDB will report progress of these tasks to the active tasks API and remove tasks as soon as they are completed or have failed.
-
-#### Example Usage:
-
-{% highlight js %}
-const tasks = PouchDB.activeTasks.list()
-{% endhighlight %}
-
-#### Example Result:
-
-{% highlight js %}
-[{
- "id": "d81fea92-8ce4-42df-bb2b-89a4e67536c3",
- "name": "database_compaction",
- "created_at": "2022-02-08T15:38:45.318Z",
- "total_items": 12,
- "completed_items": 1,
- "updated_at": "2022-02-08T15:38:45.821Z"
-}]
-{% endhighlight %}
-
-### Real-time updates
-
-You can use [JavaScript Proxies](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy) to monitor calls to the active tasks API. For the `PouchDB.activeTasks.add()` function, which is used internally to announce new tasks to PouchDB, you can monitor calls as follows:
-
-{% highlight js %}
-PouchDB.activeTasks.add = new Proxy(PouchDB.activeTasks.add, {
- apply: (target, thisArg, argumentsList) => {
- const task = argumentsList[0];
- const id = Reflect.apply(
- target,
- PouchDB.activeTasks,
- [task]
- );
- console.log('Added task', id, task.name);
- return id;
- },
-});
-{% endhighlight %}
From 6726bcc9dd05d3a4853dc1bc2db3890aacde077d Mon Sep 17 00:00:00 2001
From: Alex Feyerke
Date: Mon, 11 May 2026 10:41:15 +0200
Subject: [PATCH 10/13] feat: add 404.html page in the assumption the new
apache hosting can be set up to use it
---
docs/404.md | 23 +++++++++++++++++++++++
1 file changed, 23 insertions(+)
create mode 100644 docs/404.md
diff --git a/docs/404.md b/docs/404.md
new file mode 100644
index 0000000000..cb1fa9ffa1
--- /dev/null
+++ b/docs/404.md
@@ -0,0 +1,23 @@
+---
+title: Couldn’t find that page, sorry.
+permalink: 404.html
+layout: default.html
+edit: false
+---
+
+
+
+
+
+
+What could have happened:
+
+- A typo or a broken link. Best back to the [home page](/) and start over.
+- If you came to this page from the documentation version switcher, you might have been viewing docs for a feature that doesn’t exist in the version you switched to.
+- Something on the page is broken. If you’re sure this is the case, please [open an issue](https://github.com/apache/pouchdb/issues) so we can fix it. If _you_ want to fix it, please [open a PR at our GitHub repo](https://github.com/apache/pouchdb).
+
+👋 Have a good day!
+
+
+
+
\ No newline at end of file
From 21749cc8997b93de5e60fd2eff0393100796729d Mon Sep 17 00:00:00 2001
From: Alex Feyerke
Date: Tue, 23 Jun 2026 14:35:19 +0200
Subject: [PATCH 11/13] feat: make learn nav sidebar generic
---
docs/version/7.3.0/nav.html | 7 ++++---
docs/version/8.0.0/nav.html | 7 ++++---
docs/version/9.0.0/nav.html | 7 ++++---
docs/version/latest/nav.html | 7 ++++---
4 files changed, 16 insertions(+), 12 deletions(-)
diff --git a/docs/version/7.3.0/nav.html b/docs/version/7.3.0/nav.html
index b58de06083..e00bcce01f 100644
--- a/docs/version/7.3.0/nav.html
+++ b/docs/version/7.3.0/nav.html
@@ -1,10 +1,11 @@
{% if version %}
{% assign currentVersion = "/version/" | append: version %}
-{% else %}
+ {% assign aboutText = "About PouchDB " | append: version %}
+ {% else %}
{% assign currentVersion = "" %}
+ {% assign aboutText = "About PouchDB" %}
{% endif %}
-currentVersion: {{currentVersion}}
-{% include nav_item.html versionPrefix=currentVersion path="/learn.html" text="About 7.0.0" %}
+{% include nav_item.html versionPrefix=currentVersion path="/learn.html" text=aboutText %}
{% include nav_item.html versionPrefix=currentVersion path="/download.html" text="Download" %}
{% include nav_item.html versionPrefix=currentVersion path="/users.html" text="Who's using PouchDB?" %}
{% include nav_item.html versionPrefix=currentVersion path="/getting-started.html" text="Get Started Guide" %}
diff --git a/docs/version/8.0.0/nav.html b/docs/version/8.0.0/nav.html
index 00d1b690a0..e00bcce01f 100644
--- a/docs/version/8.0.0/nav.html
+++ b/docs/version/8.0.0/nav.html
@@ -1,10 +1,11 @@
{% if version %}
{% assign currentVersion = "/version/" | append: version %}
-{% else %}
+ {% assign aboutText = "About PouchDB " | append: version %}
+ {% else %}
{% assign currentVersion = "" %}
+ {% assign aboutText = "About PouchDB" %}
{% endif %}
-currentVersion: {{currentVersion}}
-{% include nav_item.html versionPrefix=currentVersion path="/learn.html" text="About 8.0.0" %}
+{% include nav_item.html versionPrefix=currentVersion path="/learn.html" text=aboutText %}
{% include nav_item.html versionPrefix=currentVersion path="/download.html" text="Download" %}
{% include nav_item.html versionPrefix=currentVersion path="/users.html" text="Who's using PouchDB?" %}
{% include nav_item.html versionPrefix=currentVersion path="/getting-started.html" text="Get Started Guide" %}
diff --git a/docs/version/9.0.0/nav.html b/docs/version/9.0.0/nav.html
index 99be3f7e98..e00bcce01f 100644
--- a/docs/version/9.0.0/nav.html
+++ b/docs/version/9.0.0/nav.html
@@ -1,10 +1,11 @@
{% if version %}
{% assign currentVersion = "/version/" | append: version %}
-{% else %}
+ {% assign aboutText = "About PouchDB " | append: version %}
+ {% else %}
{% assign currentVersion = "" %}
+ {% assign aboutText = "About PouchDB" %}
{% endif %}
-currentVersion: {{currentVersion}}
-{% include nav_item.html versionPrefix=currentVersion path="/learn.html" text="About 9.0.0" %}
+{% include nav_item.html versionPrefix=currentVersion path="/learn.html" text=aboutText %}
{% include nav_item.html versionPrefix=currentVersion path="/download.html" text="Download" %}
{% include nav_item.html versionPrefix=currentVersion path="/users.html" text="Who's using PouchDB?" %}
{% include nav_item.html versionPrefix=currentVersion path="/getting-started.html" text="Get Started Guide" %}
diff --git a/docs/version/latest/nav.html b/docs/version/latest/nav.html
index 7ca8c3984b..e00bcce01f 100644
--- a/docs/version/latest/nav.html
+++ b/docs/version/latest/nav.html
@@ -1,10 +1,11 @@
{% if version %}
{% assign currentVersion = "/version/" | append: version %}
-{% else %}
+ {% assign aboutText = "About PouchDB " | append: version %}
+ {% else %}
{% assign currentVersion = "" %}
+ {% assign aboutText = "About PouchDB" %}
{% endif %}
-currentVersion: {{currentVersion}}
-{% include nav_item.html versionPrefix=currentVersion path="/learn.html" text="About latest" %}
+{% include nav_item.html versionPrefix=currentVersion path="/learn.html" text=aboutText %}
{% include nav_item.html versionPrefix=currentVersion path="/download.html" text="Download" %}
{% include nav_item.html versionPrefix=currentVersion path="/users.html" text="Who's using PouchDB?" %}
{% include nav_item.html versionPrefix=currentVersion path="/getting-started.html" text="Get Started Guide" %}
From 4d11556072909c7757fc3257371aca4a04f80cf9 Mon Sep 17 00:00:00 2001
From: Alex Feyerke
Date: Tue, 23 Jun 2026 14:35:41 +0200
Subject: [PATCH 12/13] docs: update website deploy info in WEBSITE_README
---
docs/WEBSITE_README.md | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/docs/WEBSITE_README.md b/docs/WEBSITE_README.md
index 23994c5253..b122103261 100644
--- a/docs/WEBSITE_README.md
+++ b/docs/WEBSITE_README.md
@@ -34,18 +34,18 @@ By default, SW will only cache the pages you’ve seen plus `offline.html`, whic
To test the "Content updated, reload now?" toast that indicates that a new version of the site is available, run the dev server, make a change to the site (e.g. change some text somewhere), then navigate once or reload the page with a normal (not hard) reload. The update toast should show. When you click it, the page and the serviceworker cache will be updated.
-## PouchDB Version Updates
+## PouchDB Version Update Checklist
If you’re releasing a new PouchDB version, the site also needs some updates:
-1. Replace `docs/static/js/pouchdb.min.js` with your newly built version of the same file (this is the PouchDB that is hosted by the website itself and available in the browser dev console).
-2. Update the `version` key in `docs/_data/site.js` to the new PouchDB version.
+- [ ] Replace `docs/static/js/pouchdb.min.js` with your newly built version of the same file (this is the PouchDB that is hosted by the website itself and available in the browser dev console).
+- [ ] Update the `version` key in `docs/_data/site.js` to the new PouchDB version.
Then deploy the webite.
## Website Deployment
-Currently, the website is deployed manually by members of the team. In the near future, any website changes merged into `master` will be deployed automatically.
+Any website changes merged into `master` will be deployed to https://pouchdb.apache.org automatically.
## Technology Choices
From 45cf73cac2d826f4fb49675931a2de36601f6180 Mon Sep 17 00:00:00 2001
From: Alex Feyerke
Date: Tue, 23 Jun 2026 14:35:47 +0200
Subject: [PATCH 13/13] fix: small CSS fixes
---
docs/src/less/bootstrap/mixins.less | 3 ++-
docs/src/less/pouchdb/version-switcher.less | 1 +
2 files changed, 3 insertions(+), 1 deletion(-)
diff --git a/docs/src/less/bootstrap/mixins.less b/docs/src/less/bootstrap/mixins.less
index 3857f4ce5d..c5130ad35c 100644
--- a/docs/src/less/bootstrap/mixins.less
+++ b/docs/src/less/bootstrap/mixins.less
@@ -33,7 +33,8 @@
outline: thin dotted;
// WebKit
// outline: 5px auto -webkit-focus-ring-color;
- outline: 5px auto #de061b;
+ // outline: 5px auto #de061b;
+ outline: 5px auto currentColor;
outline-offset: -2px;
}
diff --git a/docs/src/less/pouchdb/version-switcher.less b/docs/src/less/pouchdb/version-switcher.less
index 2f9d3a700b..e1b8f8f0de 100644
--- a/docs/src/less/pouchdb/version-switcher.less
+++ b/docs/src/less/pouchdb/version-switcher.less
@@ -2,6 +2,7 @@
position: fixed;
bottom: 2em;
right: 2em;
+ z-index: 1000;
.btn {
padding-inline: 1em;
}